When starting a new React project, one of the most difficult steps is deciding how to manage the application state. Historically, many projects have used local state, or have used libraries like Redux, and others have implemented a custom mix between the two.
NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.
In this traditional way of managing state, as the application grows, we start adding more and more data to keep track of the application state, and this can lead to many bugs or invalid states, typescript can help immensely on this front as it enforces a state model to be respected, but still there is a lot of room for bugs to surface.
We’re going to use another pattern of managing state that aims to solve this problem, state machines, a mathematical model of computation, that describes the behavior of a system that can only be in one state at any given time.
State machines have been around for a while and, as you’ll see, using them is a more formal and deterministic way of managing state.
In this post, I’m going to show you why and how to use state machines to handle state in a React application with a lot more confidence.
What is XState?
XState is a library that allows you to create, interpret and execute state machines and statecharts. State machines are abstract models defined by a finite number of states, events and the transition between those states. Statecharts are basically State Machines on steroids, they introduce nested states, parallel states, and extended state, among other things.
When using State Machines, it’s necessary to model the entire set of States, Events and Transitions beforehand. This can seem like a tedious exercise, but it will give us total confidence in what is happening at each moment, because anything that wasn’t explicitly defined can’t happen.
Now that we’ve got the concepts down, let’s get coding.
Project Setup
First let’s create a simple React project, for that we can use create-react-app
, and setting to use the typescript template.
npx create-react-app xstate-getting-started --template typescript
Then, the next step is to install XState. xstate
is the core package, as we need this to create our state machines. @xstate/react
provides an easy way to use hooks for integration with our React components.
npm install xstate @xstate/react
The project is good to go, let’s get started creating our first state machine!
Modeling the Machine
This tutorial will use as an example a Traffic Light model, so in order to define the model, we need to go through the very basic states, events and context that a traffic light has.
In its most basic form, a traffic light has 3 states, green, yellow and red.
What events does it have? A traffic light loops through the 3 light colors, and nothing else. So we’ll only need a single NEXT event, that will trigger a transition of the Traffic light to the next color in the loop.
We’re using Typescript on this project, since XState is written in typescript, it provides first-class support to strongly type State, Events and Context, so a wise move would be to type our domain. Events in XState are what causes the state machine to transition from its current state into its next state. Our Event type is declared as a Union Type of all the possible Events.
type TrafficLightEvent = { type: "NEXT" };
The last step is to define how our state looks using something called Typestates, that is a concept where we type the possible state values, and how that value goes along with context, this can be really powerful on more complex machines, on this simple example will be used just to represent the possible state values, since the context will not be used.
type TrafficLightState = | { value: "green"; context: undefined } | { value: "yellow"; context: undefined } | { value: "red"; context: undefined };
Now that everything is typed, it’s ready to create the machine.
export const trafficLightMachine = createMachine({ id: "trafficLight", initial: "red", states: { green: { on: { NEXT: "yellow" }, }, yellow: { on: { NEXT: "red" }, }, red: { on: { NEXT: "green" }, }, }, });
Let’s go over a couple details that were not mentioned before: the id –the machine identifier — and the initial state value, which is what state the machine starts on.
The states property is defined with the possible states, and how they should handle the events that can happen. So there is a property for each of the possible states that handle that Event, and also what state the machine should transition to, when the event is triggered.
In this scenario, every state handles the NEXT event, but this does not have to be the case. For example, if the ‘on’ property is missing on the red state, the machine will be forever stuck in red, since there will be no event that can transition from it.
And that’s it, that is our completed, working state machine.
Visualizing our State Machine
Now the cool part, let’s visualize it. XState’ has its own Visualizer tool: https://xstate.js.org/viz/
Our traffic light state machine
It enables developers to see the machine, the possible states, and how events transition from one state to the other. In the visualizer itself, you can also click on the events, and that will show the state transitions, it’s really cool.
Now, let’s take our newly made machine, and implement it on a React component.
Integrating with React
import React from "react"; import "./App.css"; import { useMachine } from "@xstate/react"; import { trafficLightMachine } from "./trafficLightMachine"; export const App = () => { // Typescript will infer what current and send are here // And will provide useful information about usage const [current, send] = useMachine(trafficLightMachine); return (
); };
The first action is to import the machine and useMachine hook from the @xstate/react
package.
The matches function is what defines what state the machine is currently in, so if it is in the red state, current.matches(‘red’)
will be true.The send function acts a lot like the Dispatch does for redux, it just sends the event you want to the machine, in this case NEXT.
Running it and hitting the button, the traffic light colors will change!
Wrapping Up
Okay! Now you know what state machines are, how they work, why they are useful, and how to implement one with React. Even though this example is extremely simple, it will give you insights into how State machines can model complex business logic, and the confidence that they can give you on your projects. You can review the sample code.
Santiago Kent
Related Posts
-
React Navigation and Redux in React Native Applications
In React Native, the question of “how am I going to navigate from one screen…
-
React Context API Global State Management
Modus Create's Javascript Community of Experts shares tips for getting the most out of the…