State management in React upgraded: xstate

Siniša Nimčević
CodeX
Published in
6 min readAug 28, 2021
Photo by Luca Jonas on Unsplash

Introduction (the finite state machine concept inside React)

Finite state machines are concept that’s been around for a rather long time and has nothing in particular to do with React. I won’t go all wikipedia on you, but the main idea is that you have a finite set of states, and your app can be in one state, and one state only, in any one point in time. Provided with a certain input, the finite state machine transitions to another one of it’s states. Now this bit of distilled computer science sounds a lot like state management with an immutable single source of truth you’ve no doubt heard so much about.

What the xstate library does, is that it provides you with your very own set of utilities to create finite state machines (FSM) to manage your javascript application state. In itself, xstate isn’t biased towards any one popular library/framework — it’s just a javascript implementation of a concept, and as of now is available as a package for React, Vue and Svelte.

My immediate concern was sharing a state machine between components. Right now, the best way to share state machines between components is by use of React’s built in useContext hook.

If your’re somewhat familiar with xstate or prefer to go at your own pace feel free to checkout the github repository I used to try everything out. It has a test for the state machine and the implementation of useContext both of which are NOT covered in the content below.

The implementation

What I personally like about switching to FSM managed state is the ease of implementation. Exhibit A — this is the weight of your state machine dependencies:

The network cost of key xstate imports

Once you’ve planned out your state machine on a piece of paper like a real computer scientist, you can start writing your very first FSM in xstate. By the way, to get the idea of how to draw state machines on paper and impress your friends, checkout this handy tool you’ll be able to use to analyze your implemented FSMs. But more on that later.

This is the basis of a FSM written with xstate.

Basis of a FSM written in xstate

This particular FSM deals with fetching a set of data, you’ll see that it has

  • a human readable id,
  • we set the initial state right away,
  • it has the so called context (which is what you’ll be able to manipulate in your states section),
  • and a section detailing the states of your state machine.

That’s all there is to it. The states section is completely under your control — the state names provided in this example are entirely up to you to name — there is no framework-like constraint. Also, there’s no ugly switch case statement so common in these types of state management APIs. Everything is neatly packaged inside a configuration object.

We see that the initial state is set to idle. To transition from one state to the other we use (yes, you’ve guessed it) transitions. This is how a transition is implemented.

Both FETCH and loading are arbitrary names you chose earlier. How cool is that? So if the state is idle, if the machine receives a FETCH command, it will transition to the state called loading. For each state defined in your states, you can have any number of on objects, each being named by a command you intend to send to your FSM and each leading to the next intended state.

Now, when dealing with async code, namely your equivalent of the loading state in our example, things get ever so slightly more complicated.

The loading state has an invocation config under the invoke prop. Simplified and pruned of comments that would look like this.

Handling async state inside a an xstate state configuration.

And the invoke broken down would look like this:

The invoke configuration with source code and builtin hooks.

I hope I’ve commented this section of the code clearly, and the collapsed, simplified screenshots of the configuration should highlight key props for configuring an asynchronous data call.

Once your state machine is ready, import it into your component and use it with a useMachine hook:

Example of a use case for the useMachine hook.

That’s more or less the gist of FSMs including some slightly advanced features brought in to accommodate asynchronous code.

But how does it scale?

Well, as the complexity of your app grows we are faced with what is called a “state and transition explosion”. Thankfully, because FSMs as concept have been around since the 40s , this has been dealt with. In 1987, a dude name David Harel established statecharts. This is all detailed in David Kourdish’s talk at the CSSConf BP 2019 — what I’ll try to explain bellow is right there (only put more eloquently by the writer of the xstate library, himself), so I implore you, if you’ve come this far and have about 40 minutes to spare, do check it out.

So, to keep it short, statecharts conceptually provide:

  • ACTIONS (xstate has hook-like configuration for entry and exit events, transitions are actions by the way, etc)
  • GUARDS (you can implement conditional transitions)
  • HIERARCHY (you can have nested states)
  • ORTHOGONALITY (you can have parallel states)
  • HISTORY (you can transition back to remembered states)

If you plan to add xstate to your project, make sure you keep this in mind and find out more in the official docs.

Conclusion

The main advantages of using FSMs and statecharts in your applications are the ability to visualize your software modeling process, to create easy to reason about (and test) precise diagrams of your logic, awesome test coverage, auto generated code and most importantly, with all this, accommodation of late stage breaking requirement changes.

What might hold you back is the learning curve, and the fact that this way of thinking about your software requires planning ahead (but to be honest, not a lot of good projects happen without planning ahead). Also, there is a caveat — not everything can be modeled to use statecharts (yet).

As for myself, I’m fully sold on the idea and look forward to using xstate the next time an opportunity arises. I also hope I’ve nudged you in the right direction and that the front end dev community will start to be more aware of software modeling with techniques similar to what’s been described here. This article only scratches the surface.

If you think you have some insights into xstate I may have left out or misinterpreted I encourage you to contact me— the subject is broad and can be approached in a myriad of ways. If you want to reach out or just grow your network — you can find me on twitter and linkedin as well.

--

--

Siniša Nimčević
CodeX
Writer for

All things .js - husband, father and aspiring techie