Selecting a finite state machine library for React
What is this article about and what motivated me to write it?
This article is going to look at different libraries providing finite state machine abstractions that can be used in React applications. I have used state machines on the backend in the past but I have never used one in a React application. I’m also going to share why I believe our codebase could benefit from using it. Let’s start with that.
The Rainforest automation editor needs a refactor
One of the most complex parts of our frontend codebase is the test editor for the Rainforest no-code testing product. The related code has been through a lot of changes in the past 2 years and its complexity increased as time passed. Apart from other data structures, we store a lot of boolean flags in the editor state. We use them to dispatch side effects and to inform the user about what is currently happening in the editor.
The amount of boolean flags increased with the overall complexity of the editor and it became hard to work with them because making a decision on what to do next or what to render in the UI often requires combining many of them. The resulting boolean salad became hard to read and reason about. It became obvious it was time to take a different approach if we wanted to keep our sanity. Here is one really bad example of what I’m talking about:
const isDeleteDisabled = hasPendingUpdate || isBlockSelectionEmpty || isReplayingActions || isReplayingInitialNavigate || isActionSelectionIndeterminate;
So what is a better alternative to the boolean salad we’re currently maintaining? I believe state machines are a great candidate for describing all possible states of the editor and transitions between them. These states and transitions are currently somewhat scattered throughout the codebase and getting an overall picture of what happens when and what are all the possible combinations requires a lot of jumping around. Making a mental map of all the possibilities is therefore difficult and that also means it’s difficult to onboard new developers. The ability to quickly analyze the application states and transitions between them can also be beneficial while collaborating with project managers, designers, customer support represantitives or anyone else interested in the product functionality. A diagram of the state machine can be a great addition to feature documentation.
As mentioned before I haven’t used a state machine library in React yet so I wanted to look around to find out what options are available. The only one I was aware of before starting a deeper search was XState from David Khourshid. It seems to be quite popular and successful but I like to look around for alternatives before committing to the most hyped option.
So is there anything else apart from XState?
There are a few alternatives out there. Searching the web for React state machine mostly returns
XState and sometimes
Let’s summarize a few basic stats about the libraries I’ve found. The data was collected on 26th January 2021.
|Name||Github star count||Last updated||Bundle size*||Comes with React friendly integrations|
|xstate||14.5k||hours ago||16.7 kB||Yes
|robot||1.2k||hours ago||1.2 kB||Yes
|overmind||1.2k||hours ago||10.4 kB||Yes
overmind-react provides both hooks and HOCs
|stent||675||5 months ago||3.3 kB||Yes
part of bundle
|machina.js||1.9k||2 years ago||27.1 kB||No|
* Minified + gzipped size from bundlephobia.com
machina.js aren’t actively updated anymore and they don’t come with React integrations out of the box which disqualifies them from further investigation.
stent doesn’t seem to have enough community traction and it only provides an HOC for connecting the state machines to React components. The HOC only integration isn’t really much of an issue, but I slightly prefer using hooks where possible. Less active development and popularity are my main reasons for not diving deeper.
overmind looks interesting but it is providing much more than just state machines. From briefly scanning through the docs, I would say it is a
redux alternative with built in support for state machines and a state chart addon. We’re using
redux in our application and we currently don’t plan to replace it which is why it doesn’t seem like the right choice, it would be overkill considering we only need a state machine abstraction.
That leaves us with
robot. Let’s look at these two in a bit more detail.
XState vs Robot
The first obvious difference between these two is the bundle size, Robot is much smaller at 1.2 kB minified and gzipped compared to XState which takes up 16.7 kB. Keeping the size minimal is an intentional constraint of Robot. I think that these two pages from the library authors sum up their goals nicely:
Both Robot and XState share these similarities:
- Extended state (context) makes it possible to keep non-state values within the machine.
- Both can invoke external functions as well as other state machines to perform subtasks.
- Both support guards which allow the addition of custom logic to prevent state transitions from occurring.
XState comes with more functionality built in:
- Serializing state machines into JSON format easily.
- Supports the actor model as an alternative to invoking machines.
- Comes with a visualizer tool that gives a visual representation of the state machine.
- Conforms to the SCXML specification. This standard defines a XML schema for describing state machines, and supporting it means it’s theoretically possible to import and export the XML for a XState state machine so it can be used in multiple languages.
So what are we going to use for the Rainforest automation editor refactor? The XState visualizer is attractive, but I don’t see the need for the other advanced features that XState provides which is why I’m currently leaning towards using Robot. I like the functional based API that allows for easy composition and I also like that its bundle size is so lightweight.
State machines can help with improving the readability and maintainability of UI state management logic. We’re looking into using them in the most complex parts of our application but that doesn’t mean they can’t be useful for simpler components. There are many articles describing their advantages, I’ve linked a few below in case you’re interested in getting a deeper understanding of why and when state machines are a good choice. I hope you found the article helpful in choosing the right library for your use case.