This is to serve as a Rosetta Stone of state management systems. A basic packing list app was built in:
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.
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! 🔥
Also: Watch the talk from Connect.Tech here:
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.
@observer is like an automatic `mapStateToProps` + `reselect` if you’re used redux things — Steve Kellock
Be sure to checkout Nader’s blog post highlighting some more advanced topics on switching to MobX.
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.
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.
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) 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.
ReduxX, while probably having a bit of trouble in SEO, is still a pretty cool name.
“Why is it you can add X to something to make it cool?”
— Gant X
ReduxX reads pretty well as in some ways it reminds me a bit of the charisma from Unstated, as we’re using react-styled verbiage to set and mutate state. One aspect that might seem alien, is the state is retrieved with
getState as a function. This feels a bit like keychain access, and I wonder if there could be some certs/crypto mixed in easily? Food for thought. I see there’s
obscureStateKeys: true which will swap keys out for GUIDs. Security-wise this library might have some interesting advantages.
As for how to use it, Set and Get via keys. That’s it! If you’re not worried about middleware, and you’re familiar with keychain globals, you already know ReduxX.
Special thanks to the author Mikey Stecky-Efantis for providing this example!
100% test coverage, MIT licensed, TypeScript backed mini-library for state management. This library feels the closest to “Just use globals”. As a matter of fact, if you identify that
update takes the signature of
setState then you’re looking at a global state in a singleton.
Special thanks to Arthur Gunn for contributing this 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 😆
UPDATE: Since this writing, many people have contributed!!! I’m especially thankful and happy that in some cases, people used this library to learn that their state system did not work with React Native, and the fixed it upstream!!!To get a full listing of state systems visit the repo.
Do you want no dependencies?You can get pretty far with setState and Context! Why bring in a dependency at all? If you’re not sure your app will need it, try to ship without an external lib. You might be able combine Context with something simple like react-automata to round out a clean and testable result!
**Do you want simple?**unstated, reduxX, and pure-state are VERY easy and simple. They have slight opinion differences, and small differentiating benefits for each. MobX is also easy, but shows up with some decorator syntax you have to accept. If you do, then the code stays readable and optimized, with a TON of docs/stackoverflow help.
**Do you want scalable?**If you’re sure your app is going to need all the bells and whistles, it’s time to break out the big guns. This is where Redux shines. You’re forever a mechanic when you implement Redux, so you know you can adapt. MobX-State-Tree shows up with all the powers of MobX + opinions, state, optimizations, and more. It’s not something you can comprehend in a single sitting, but each time you learn more about it, you’re empowering your application.
Do you want the future?There’s no doubt that GraphQL is catching fire in the technical space. Now if you’re using it with AppSync for networking, or if you’re simply using apollo-link-state to manage your local data, you surrender some fine-grained control, but gain power in return. Be sure to keep an eye on these libraries as they evolve. It’s likely that many of the state-systems above will have to adapt to accommodate GraphQL in the near future.
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.