DEV Community

Abhay Singh Kathayat
Abhay Singh Kathayat

Posted on

Mastering State Machines with XState in React

State Machines with XState

XState is a JavaScript/TypeScript library for state machines and statecharts, which is especially useful for managing complex states and transitions in applications. While React's state management often uses useState, useReducer, or external libraries like Redux, XState introduces a formalized way to handle application states using state machines.

State machines provide a structured and predictable way to define state transitions. With XState, developers can define complex state logic in a declarative way, making it easier to manage states and transitions, especially in larger applications.


1. What is a State Machine?

A state machine is a computational model used to represent the states of a system and the transitions between those states. It has:

  • States: Different stages or modes in which the system can exist.
  • Events: Actions or inputs that trigger state transitions.
  • Transitions: Rules that define how the system moves from one state to another when an event occurs.
  • Actions: Operations that occur when a state transition happens.

For example, a simple traffic light system could be modeled as a state machine:

  • States: Green, Yellow, Red
  • Events: Timer expires
  • Transitions: From Green to Yellow, from Yellow to Red, from Red to Green
  • Actions: Change light color.

2. Why Use State Machines in JavaScript/React?

State machines can help:

  • Ensure predictable state transitions: State machines are designed to have clear rules about how to move from one state to another, reducing bugs caused by unpredictable state changes.
  • Improve maintainability: By clearly defining states and transitions, it becomes easier to maintain and extend the system, especially in large applications.
  • Visualize state logic: XState allows you to visualize your state machines, which can help teams understand the system's flow more easily.

In React applications, state machines can help manage more complex interactions, such as form validation, UI states, user authentication, and workflows.


3. Getting Started with XState

To begin using XState, install it in your project:

npm install xstate
Enter fullscreen mode Exit fullscreen mode

Once installed, you can start defining your state machine. Let’s look at a simple example using XState.

Example 1: Basic Traffic Light State Machine

import { createMachine, interpret } from 'xstate';

// Define the state machine
const trafficLightMachine = createMachine({
  id: 'trafficLight',
  initial: 'green', // initial state
  states: {
    green: {
      on: { TIMER_EXPIRED: 'yellow' },
    },
    yellow: {
      on: { TIMER_EXPIRED: 'red' },
    },
    red: {
      on: { TIMER_EXPIRED: 'green' },
    },
  },
});

// Interpret the machine (create an instance)
const trafficLightService = interpret(trafficLightMachine).onTransition(state =>
  console.log(state.value) // logs the current state
);

// Start the service
trafficLightService.start();

// Simulate events
trafficLightService.send('TIMER_EXPIRED'); // yellow
trafficLightService.send('TIMER_EXPIRED'); // red
trafficLightService.send('TIMER_EXPIRED'); // green
Enter fullscreen mode Exit fullscreen mode

In this example:

  • We define three states: green, yellow, and red.
  • Each state transitions to the next when the TIMER_EXPIRED event is sent.
  • The state transitions are logged in the console.

Explanation:

  • State: Represents the current status of the system (e.g., green, yellow, red).
  • Event: An action or condition that triggers a transition (e.g., TIMER_EXPIRED).
  • Transition: The rule that changes the state based on the event.

4. Integrating XState with React

XState integrates seamlessly with React, enabling you to manage component states in a more structured and predictable way.

Example 2: Traffic Light in React using XState

import React from 'react';
import { createMachine, interpret } from 'xstate';
import { useMachine } from '@xstate/react';

// Define state machine
const trafficLightMachine = createMachine({
  id: 'trafficLight',
  initial: 'green',
  states: {
    green: {
      on: { TIMER_EXPIRED: 'yellow' },
    },
    yellow: {
      on: { TIMER_EXPIRED: 'red' },
    },
    red: {
      on: { TIMER_EXPIRED: 'green' },
    },
  },
});

function TrafficLight() {
  const [state, send] = useMachine(trafficLightMachine);

  return (
    <div>
      <h1>Traffic Light</h1>
      <div style={{ width: 100, height: 100, backgroundColor: state.value }}>
        {state.value}
      </div>
      <button onClick={() => send('TIMER_EXPIRED')}>Next</button>
    </div>
  );
}

export default TrafficLight;
Enter fullscreen mode Exit fullscreen mode

In this React component:

  • The XState machine is created for the traffic light.
  • The useMachine hook connects the machine to the React component and provides the state and send function.
  • The send function triggers state transitions (e.g., on clicking the button).
  • The state.value is used to control the background color of the traffic light, visually representing the current state.

5. Advanced Features of XState

a. Hierarchical State Machines (Statecharts)

XState allows for hierarchical (nested) states, which is helpful when you need to represent states within states. This is useful for managing complex UI interactions or workflows.

const machine = createMachine({
  id: 'machine',
  initial: 'idle',
  states: {
    idle: {
      on: { START: 'working' },
    },
    working: {
      initial: 'waiting',
      states: {
        waiting: {
          on: { NEXT: 'done' },
        },
        done: {
          type: 'final',
        },
      },
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

b. Actions and Services

  • Actions are used to perform side effects or operations when entering a state or transitioning between states.
  • Services allow you to integrate external processes (e.g., fetching data from APIs).
const machine = createMachine({
  id: 'fetchData',
  initial: 'idle',
  states: {
    idle: {
      on: { START: 'loading' },
    },
    loading: {
      invoke: {
        src: 'fetchData',
        onDone: { target: 'success', actions: 'setData' },
        onError: { target: 'failure', actions: 'setError' },
      },
    },
    success: { type: 'final' },
    failure: { type: 'final' },
  },
},
{
  services: {
    fetchData: () => fetch('/data').then(res => res.json()),
  },
  actions: {
    setData: (context, event) => { context.data = event.data },
    setError: (context, event) => { context.error = event.data },
  }
});
Enter fullscreen mode Exit fullscreen mode

6. Advantages of Using XState

  • Predictable: State transitions are explicit and deterministic, reducing potential bugs in the application.
  • Scalable: XState allows for the creation of complex state machines, even for large applications with many states and transitions.
  • Testable: State machines are highly testable. You can test transitions and events independently of UI components.
  • Visualizable: XState comes with built-in tools like the XState Visualizer, which helps you visualize your state machine and better understand the flow of your application.

7. Conclusion

XState is a powerful library for managing complex state logic in React applications. By using state machines, you can ensure that your state transitions are clear, predictable, and maintainable. Whether you're building simple UIs or complex workflows, XState can simplify your state management and help you create more robust and scalable applications.


Top comments (0)