Statecharts and state machines offer a promising route to designing and managing complex state in apps. For more on why statecharts rock, check out the first article in this series.
One of the main reasons why stat charts have not grown in popularity within the front-end world is that best practices have not yet been established. It is not immediately clear how to use state machines with popular component-based UI libraries such as React, Vue, or Angular.
While it is too early to announce best practices for statecharts in JS, we can trace some of the patterns used by existing state machine integration libraries.
Statecharts serve both for visual design and the underlying code for graph-based state machines.
Keep in mind that we are in the early days of using statecharts with JS, and it might be worth experimenting with different libraries or even developing your own. That being said, XState is currently leading the pack for the statechart machine library in JS.
The above state machine code can produce a much more readable statechart diagram when passed as JSON to the XState visualizer.
You can also work the other way around, starting with designing visually and then exporting to Xstate configuration using sketch.system. We don’t have all the pieces in one place yet, but there are no serious technical barriers to an open source solution.
Stateless machines offer a non-negotiable blueprint for state management – a kind of “roll your own” solution that doesn’t dictate where or how state is stored in your application.
Like a representational component, a stateless machine is composed of pure functions, is immutable, and holds no state. It doesn’t track past, present or future – but it can be used to help you calculate each.
Managing your state can be as simple as storing it in a local state variable.
Stateless machines don’t give you much out of the box. To trigger a transition, we must always pass in the current position node to find the next one. XState can tell you which actions should be fired on each state change, but you’ll have to find a way to manage the actions yourself.
If you’re interested in a more complete solution, consider making your state machine stateful.
A stateful machine tracks the position of your node on the state graph and manages the firing of actions. There’s no need to pass transitions to the current state – it tracks your current state node.
A stateful component, as you can imagine, manages state within the component itself, or within a wrapping higher-order component. In react, it will be in the form of state. Storing the state within the UI library ensures that changes are not left untouched and will trigger re-renders.
This is the approach of a library called react-automata which uses a higher-order component introduced by statcharts.
But be careful, this is by no means best practice. In the above example, integration has tied the StateChart to the component, leading to poor separation of concerns.
Surely, there has to be a more natural way to set up conditional rendering without turning all your renderers into if/else and switch statements.
React-automata provides a pattern for conditional rendering of child components using React’s context and a <state> component. Note that the value property can match on a string, array of strings, or even a glob-based pattern.
If the state value matches the ringing, the children inside the state component will render. Otherwise nothing.
State from context can help illustrate the number of possible finite state view combinations. As in the case above, it is clear that there are only two possible configurations.
If view configuration starts to get out of hand, React-Automata provides a render prop pattern that passes in a boolean by value.