The React State Museum
- 2018-05-08 08:04 AM
View the hottest state management libs for React
This is to serve as a Rosetta Stone of state management systems. A basic packing list app was built in:
- React 16.x Context
- Redux — by Dan Abramov
- MobX — by Michel Weststrate
- unstated — by Jamie Kyle
- MobX-State-Tree — by Michel Weststrate again
- Apollo GraphQL with Amazon AppSync
- setState + react-automata — by Michele Bertoli
- Freactal — by Dale while at Formidable
Surely you’re familiar with one or more of the aforementioned systems, and now you can leverage that knowledge to better understand many others. It’s your chance to see what all the buzz is about, and honestly, how similar all these state systems really are.
To portray these systems in a terse and understandable form, the chosen app is a simple packing list app with only the ability to add and clear.
Simple as it gets app (Native and Web)
To illustrate state jumping the wire, the ADD/CLEAR is one component, and the LIST is a secondary component in all examples.
Even the two main components (adding/listing) have been abstracted to an imported library, leaving only fundamental code in order to emphasize state choice(s). The code is meant to be minimalistic.
Welcome to the React State Museum!
The code for each of these systems can be found in React and React Native.
Use the above repo to personally dive into each of those systems and check them out! 🔥
Personal Notes on Each Solution:
If you want code, check the GitHub, if you want opinions, continue into this very long description below.
Here I jump into the differences between each item in the museum, and that which makes it unique. If you’ve got some strong opinions, or experiences, please share them in the comments. I’m also interested in giving this report as a fun-filled conference talk.
Here’s the most basic structure of state management, it depends only on the fundamental understanding of components and their encapsulation. In many ways, this is a great example for beginners in React. Explicitly raising state to a root component that all components are children of identifies the props vs. state relationship. As an application grows, explicit connections down into components would be more and more complex and fragile, which is why this is not commonly used.
There’s been a lot of buzz about the updates to Context. In fact, the final form of Context in 16.x feels a bit like a state management system itself. For simplicity, the context allows for provider and a consumer. All children of a provider will have access to the values applied there. All non-children will see the context defaults. The following graph explains such lineage.
Only children inherit
On a second, and very opinionated note, I’m not a fan of the consumption syntax structure. Clearly, it’s a function that is the child of Consumer, but it feels like it violates JSX while mega-overloading all use cases of braces.
A pedantic issue, but the readability of code should always factor into API, and on this front, Context starts to feel a bit dirty.
I’ll dare say at the time of this writing Redux is the most popular state management tool, and therefore the most attacked. Writing a solution in Redux took many files, and almost double the lines of code. But to Redux’s defense, it’s simple and flexible.
If you’re unfamiliar with Redux, it’s a functional approach to state management that provides time-travel and clean state management in a form like a reducer function. Dan Abramov’s video explaining redux has been watched many times.
In short, it’s like having someone shout commands in your app (Actions) which are projected via Action Creators. Data managers in your app (Reducers) hear those shouts, and can optionally act on them. I love my pirate ship analogy, so shouting “MAN_OVERBOARD” can tell your crew counter to subtract the staff by one, the accountant to re-split the treasure, and the guy swabbing the deck can just ignore it because he doesn’t care.
I like this analogy, because shouting is a powerful way to manage all corners of your app, and in larger applications, noisy. Combine this with no way to handle side-effects and the need to glue on an immutable structure to make it all work, Redux is the bill-by-hour developer’s friend.
MobX is one of the EASIEST state managers to get started with. Open the readme, and follow along and you’ll have things running in no time. It feels like mutable JS, and it really kind of is. The only part that might throw you for a loop is the decorators like
@observer on classes. Though odd, they kind of clean up the code a bit.
Be sure to checkout Nader’s blog post highlighting some more advanced topics on switching to MobX.
Ditching setState for MobX - Nader Dabit - Medium
In late 2017 I worked on a React Native project with a team that had used MobX as their state management library. I had…medium.com
In summation, MobX was one of the smallest and simplest tools to add!
It’s simple, you create a container, and inside that container, you manage state. Simple known functions like
setState exist inside the state container. It’s not just an apt name; it’s an apt React based manager.
I’m not sure how well it scales or handles middleware etc. but if you’re a beginner to state management MobX and Unstated are the simplest tools to get up and running!
Yes, this is VERY different from vanilla MobX. It’s a common misconception.
Even my co-workers try to shorten the title down to “MobX,” and I’m always pushing MST as an alternative instead. With that being said, it’s important to note MobX-State-Tree sports all the great features of Redux + reselect + Side-effect management and more all in one opinionated bundle with less code.
In this small example, the only thing that’s obvious is the terse syntax. The lines of code are barely bigger than our original MobX example. Both share that succinct decorator syntax. Though it takes a bit of time to really get all the benefits out of MobX-State-Tree.
The most important note is that if you came from ActiveRecord or some other kind of ORM, MobX-State-Tree feels like a clean data model with normalized relations. This is a great state management tool for an application that will scale.
Apollo GraphQL and Amazon AppSync
If you haven’t jumped on the GraphQL train, you’re missing out. Apollo GraphQL + AppSync is a great way to manage your state, AND handle offline, AND handle fetching API, AND handle setting up a GraphQL server. It’s a serious solution. Many have projected GraphQL to effectively “solve” the state debate. In a lot of ways that’s easy, and in a lot of ways, that’s hard.
Not everyone is ready to use a GraphQL server, but if you are, then AppSync is an easy way to handle all your data in your DynamoDB. It takes more time/energy to get this up and running, but with clear benefits.
In my example, I don’t really use all the bells and whistles. You can see the delay as the data awaits from the server, and I’m not using subscriptions to get updates. This example could get better. But it’s as simple as wrapping the config with the components. Tadaaaaa! The rest is history.
Special note: Please be careful what you put in the packing list in this example, as it’s shared.
setState + react-automata
This is a strange one in the group. In many ways, you’re wondering how setState is involved, and the answer is simple. The idea of breaking state down into a state-machine is very different from most state management systems.
By creating an xstate machine config, you handle how state gets passed, called, and identified. Therefore, you must identify ALL states your app can be in, and ALL ways it can move from one state to another. Much like dispatching an action in Redux, you have to
transition to another state on a given event.
It’s not a full state management system; it’s merely a state-machine for your state management.
Exciting benefits come from using statecharts. Firstly, you can be protected from transitions you don’t want. For instance, you can’t transition to “loaded” state without first typing text. This stops empty adds to our packing list.
Secondly, all transitions of state can be automatically generated and tested. With one simple command, multiple snapshots of state are generated.
CAVEAT: On React Native I had to
yarn add path to satisfy some unused import in a dependency. This was a sneaky gotcha for native only
Of course, we’ll feature the awesome work of Formidable Labs. Freactal is a very advanced example and states it can replace
[redux-saga](https://github.com/redux-saga/redux-saga "redux-saga")and more.
Though this was probably the most difficult one for me to setup, I still see it has great value. More examples would have helped. Special thanks to Ken Wheeler who agreed to answer any questions I had while reading through the docs.
The final code is succinct and straightforward. It feels a bit like the Context syntax in the end. I especially like the use of name-spacing
effects separately from
computer though there’s not much stopping you from taking this convention to other libs.
The Missing State Example?
I’m sure there are other state managers out there which are being sorely under-represented here, and if you know them, please send a PR to the public repo. I’ll happily accept contributions so that we can all benefit. I’ll even update this blog post as new systems are added. So please, file tickets, and more importantly contribute! The museum thanks you 😆
Gant Laborde is Chief Technology Strategist at Infinite Red, published author, adjunct professor, worldwide public speaker, and mad scientist in training. Please clap/follow/tweet or just say hi to him at a conference.
Learn React Online