Use Redux! Use MobX! Just use
Redux is functional! MobX is reactive!
setState is built-in!
But Redux has so much boilerplate! But MobX is more difficult to debug! But
setState doesn’t scale!
Why don’t you just use Rematch to abstract the Redux boilerplate? Do I use redux-thunk? redux-saga? redux-observable?
How about using Unstated? How about just using the Context API?
There are a lot of options for developers regarding how and when to use state management libraries (SML).
Remember those last two articles about the massively underplayed game library? (The Joy of Forms with React and Formik & One Router to Rule Them All: React Router) There was a third part to come — a follow-up meant to implement an example of state management for an application. The application was small, granted, but this somewhat contrived example would tie it all up. It’d be the cherry on top. It would complete the rule of threes. 😉
There is not going to be another tutorial about which state library we can use to better manage our game library. Rather, in this post, we’re going to look at how and why you might use a library to manage your application’s state, and why that particular application is a perfect example of when you might not need a SML at all.
When I started to embark on the journey to learn React, I came to the same conclusion that many developers seem to when Googling around for examples and articles — Use Redux!
This is a mistake I learned the hard way.
We both did.
That’s not to say that state management is some sort of pariah. It is, after all, built into React itself. Using
setState is a perfectly manageable way to get started.
However, as I alluded to above, there are many choices. Fortunately, this means you can find one that fits your needs. Unfortunately, there are no clear winners. It comes down to preference. Let’s take a look at a few dominant options.
This is the go-to. Or maybe Google makes it seems that way. Of all state-management libraries, this is the one that you will probably see the most.
It will be pointed at for its functional style, its boilerplate, and its time-travel debugging (however often or not you happen to actually use it). I believe it to be cumbersome, and often overkill in most cases.
But, I also do not work at Facebook and haven’t yet needed the Great Decoupling it provides from your components. When I say components, I mean container components. That is to say that the types of state you use Redux for should ideally span your container components and sections of your application at a high-level. This is what it was made for.
MobX is schmancy. No need for smart or dumb components — you just hook up your values, fire your modifiers (and side-effects/reactions) and it does the heavy lifting for you. It’s quick to work with and handles data more like a spreadsheet on steroids.
In practice, this results in a bit more black-boxiness than other approaches, as your computed values can depend on multiple observable sources and you must exercise discipline in how you wire things together. This lends itself well to reactive programming and is a lot of fun to work with.
This library goes above and beyond the normal setState but also not as far as other libraries. I like that it uses render props, which entails a pattern of wrapping components in other components, and exposing functionality through functions. It takes a little getting used to, but it is a powerful pattern. It bases state in the React Context API, which is what enables state to be available in multi-level component hierarchies while preventing “prop drilling.”
Mentioned in passing above were a couple concepts that are common issues in React — container/presentational components and prop-drilling. These two things will drive your application architecture more than anything else.
And, to be honest, although I’m talking about React libraries, this general advice about structure is applicable to any client-side framework where components are the main unit of organization…
The concept of container and presentation components is a common pattern wherein a common component handles the data and actions associated with the data, and merely passes the data and functions down to its children to use to display and interact with that data. This provides a clean interface for data fetching and API calling, and is easier to organize in larger applications.
Prop-drilling occurs when you have props that need to reach multiple levels down in the component tree. Perhaps it’s a piece of data like a username, or a flag for application logic like
isLoggedIn. When it needs to be actionable in different places, it needs to be passed down and acted upon in multiple components. This can be tedious and frustrating, especially as the application is evolving and the specific contracts between components are not yet set in stone.
These two concepts together can compound each other. But, by structuring your component trees in a certain way, you can greatly reduce the pain of handling state among them. There may be times when multiple layers of components need access to the same state. In these cases, take a look and see where that state is needed. You might find that some slight reorganization alleviates your issues. You may realize that you didn’t need things spread out as much as you thought.
Seriously, it might be all you need…
If you follow the Twitters and Mediums, it can seem like there is a lot of New & Shiny ™ out there to utilize. Remember to take a step back and see if you’re utilizing a tool that can do you some good, or if you’re riding the #hype 🚂…
In my experience, it’s not so common to have truly application or component-wide state apart from perhaps authentication and settings (you know, user stuff). Even those can be easily imported as their own classes or abstracted away (ahem, amplify), so a true state-management library may be more than is needed.
Take stock, modularize, and consider your options. There are many, and as I said before, it comes down to preference and scale. Give them a shot when you start to feel that
setState is no longer meeting your needs, and you can rest assured knowing that you didn’t rush into anything.
I want to mention one special case in regards to state management, and that is using Apollo (a GraphQL client library)— this gives you the option of merging it with your data fetching library so you can get rid of Redux or other SML.
This seems to hold a lot of promise and is something I’m watching very closely. With the release of Apollo 2.1, it looks simpler and possibly easier to handle your data fetching and application state (or what little you may need) in one fell swoop. This is an exciting idea and I look forward to sharing my findings.
More on that to come…