How to Get Rid of the Complexity in React State Management
Last April, our developers visited the spectacularly massive React Amsterdam conference, with a whole plethora of interesting speeches on applied topics. As the stack of technologies is basically settled, the speakers talked about ways to solve more practical problems, rather than promoting something unfamiliar and revolutionary. The article below focuses on Michele Bertoli’s (Front End Engineer at Facebook) speech “setState Machine”.
The speech was devoted to the main problem – the complexity in managing the state in React.
For example, let’s implement the well-known functionality Infinite Scroll – data loading when the user scrolls to the end (or almost to the end) of the page. There are many useful packages that solve this problem, but often you’re going to end up coding it yourself.
The steps you have to follow in our component:
- Attach an event handler to scroll.
- Add a test, whether user scroll to the desired point from which we will load the data.
- Fetch more data.
In the first approximation, this is enough, but let’s add a few more requirements for the correct operation:
- Do not load new data, if previous data chunk is still loaded.
- Somehow inform the user that the data is being loaded – show loading or something like that at boot time.
- Do not start uploading data, if everything is already loaded.
- Add error display for user
Let’s imagine what should be stored in our state, in addition to data, to make our functionality works correctly:
- The flag isFetching – shows us that the data is now loaded.
- The error field must contain information about the error.
- The isEmpty field shows us that there is no data.
- If you would like to add functionality for retry, then you need to store the information for it.
The main disadvantages of such realisation:
- Link to the context. A lot of conditions. For example, we load the data only when we have scrolled to the desired place, while the previous data is not loaded, etc.
- The code is difficult to read and understand.
- Hard to scale. When adding a new feature to a state, you need to walk through all our conditions and understand how to change the state in one place or another and not to break the logic. This can also lead to bugs.
State Machine can help you fix these disadvantages. In fact, this is the principle of using the Final State Machine, an abstract model containing a final number of states.
The model is described with 5 parameters:
- All possible conditions machine.
- A set of all input data received by the machine.
- Transition function – takes the previous state and a set of input data, returns a new state.
- Start State
- Final State
At each point in time, only one state can be active.
Accordingly, we can determine the conditions for transition from one state to another.
As an example, let’s consider the workings of a traffic light. This is a machine with three states. We know their order, and we can also name the initial and final state conditionally.
Let’s add to our code the library react-automata – the abstraction of the state machine for React. This is a wrapper over another xstate library – functional JS state machines without state saving and state diagrams.
To understand how this theory is applied to our case, let’s see how this functionality will look like statechart.
First, we indicate the initial state – the entry point, which is the addition of a scrolling event. When we are ready for further work, we send a READY event to the machine, and it proceeds to boot state. If the download was successful and until all the data is loaded, we go to the listening state. When the condition of loading a new piece of data is executed while scrolling, we go into the loading state. We can stay in this cycle until we load all the data. Then we no longer listen to the event.
We use hoc withStatechart from react-automata, send our start data, and now method transition for changing the machine condition and machineState – the current state of machine – are available in props.
Variable statechart — is a programme description of our picture.
Benefits of this approach:
- Fewer bugs.
- Easy to read and understand the code.
- Differentiation of what happened and when it happened. The first is controlled by the component, the second is controlled by state diagrams.
Useful links:
- Michele Bertoli’s speech on React Amsterdam 2018
- React Automata
- xstate documentation
- Explanation of state diagrams