Understand React State Management
Which Library Should I Use?
What state management library should you use with React? This is a question I have heard hundreds of times as a react developer. This article will help you answer this question.
First, we have to do a little bit of history. React was introduced in May 2013. Its paradigm shift was that your UI was a function of your state. Given some component’s state, React can determine how that component will look. From the beginning, React was built upon the concept of state, but state management in React has long been one of the hardest parts to understand.
We first saw the Flux architecture in 2014. This pattern was first suggested by Facebook and then popularized by a library called Redux. During this period, there was a bunch of different Flux-like libraries, and Redux was the one that won. If you look at this graph of the NPM trends, you will see that Redux is still extremely popular and widely used.
This graph does not cover everything obviously, but at least gives you a ballpark to understand that Redux is still used today. You might have also noticed that I was comparing other libraries in that graph, some of which you may or may not think are related. This is because when Redux came out, it encapsulated a lot of different types of state.
To help explain this, let’s define two important terms — UI state and server caching state. UI state is used for controlling interactive parts of our application like modals or even light mode or dark mode. Server caching state is where we call some API, return a response, and then we cache that result for use on the client side. Take a look at the image below.
In this example with SWR, which is a library for doing that, you see that you would call this API user, fetch some data, and then the response ( ‘ /api/user/123’ ) is cashed for use in multiple places across our client-side.
In the early days of React, a lot of this state management was just fetching some API, storing the result, and then using it somewhere in your application, mostly that server cash state. Because of this, the community leaned heavily on Redux because there was no library just for that server cache state, but that all changed with React Hooks.
Having an easy way to encapsulate logic and share it across your application, new libraries like SWR and React-query emerged that allowed you to just manage that server caching state. Server caching state is a completely different problem than UI state, and when you conflate that inside of one library, things start to get a little messy. There are lots of complexities with resiliency around network requests, and it’s best to defer to the experts and use a library focused specifically on that.
Another big shift in the React state management history was React Context. With React 16.3, we had a first-class solution to share logic across multiple different components. This prevents passing values down from component to component to component, which some people refer to as prop drilling. Keep in mind that React context in and of itself is not a state management solution. When you pair it with hooks like useState and useReducer, then it becomes a full state management solution. React context is just the means to transport your data between lots of different components.
Hopefully, that history gives you a better understanding of how we have got to today in 2021 and the current state of state management in React. Now that we have grown to understand the different types of state, we have more granular libraries for specific use cases.
The first one of these is state machines. State machines are just computer science topics. They are not anything React-specific, but they do help us solve modern state management problems with React.
A state machine is just an explicit model of your state over time. Think about traffic lights as a real-world example. It goes from green to yellow, to red, but it never goes from green to red. I think the reason that state machines have gotten popular is that having to explicitly write out all of your state helps you think through edge cases and probably uncover some bugs that you missed.
I want to talk about why so many state management libraries have been created in the React ecosystem for a bit. Let’s take a look at Figma, which is a design tool in the browser, and it’s a great example of a good use case for complex state. So if you click on one of the elements you might be having in the middle of your screen, you will notice that the concept of local state does not make sense. You have toolbars on the right and left, and when you change anything in these toolbars, it is going to have to update the middle as well. While doing that, it needs to be also very, very performant.
Creating web applications like Figma, that have complex state management needs, required developers to think outside the box for how they handle state management. This is what has led to a bunch of different libraries and experiments to try to figure out the best way to handle that state management.
Another interesting debate in the state management world is the concept of Immutable state. On one side, you have the immutable camp that says that if you directly mutate that state, you will end up with more bugs. On the other side, you have the mutable camp that says there is a whole bunch of boilerplate just to do that. In reality, both sides are somewhat right. Direct manipulation will always be less safe than indirect, but it is a trade-off between convenience and risk. So it is really up to your team.
What’s interesting though, is solutions like Immer, which allow you to write mutable code, but then execute it immutably. The basic idea of Immer is that you have a current state, and you want to make some changes.
Photo by Author
It produces this temporary draft state, which is a proxy of what your current state is. So then it understands the changes that you want to make, and it produces that next state based on what those mutations are. In reality, this means that you get a code in a mutable fashion that produces an immutable result.
One more type of state to quickly touch on is URL state. Let’s say you are on a book e-commerce store searching for a React book, and you want to filter by four stars and up. you will notice that when you do this, that state is preserved in the URL. So if you refresh the page and share it with somebody, they will always see the same state as you — looking at React books filtered by four stars and up.
You might have heard people talk about performance issues with React context as a state management solution. What they are talking about is that if you have a really large application and the entire thing is surrounded by a context provider, anytime you make a change to that state, it is going to re-render everything nested under that provider.
This can make interactions feel slow, which is why you often see people do performance optimizations by adding something like useMemo, which does not re-render components if the values never change.
Looking towards the future of React state management, the React core team has proposed a new hook called use selected context, which will handle this performance optimization for you. I am excited about this because I think that having good defaults in the React standard library will go a long way for people who are just learning how to use React.
Which state management solution should you use? Well… It depends. I know that’s kind of a cop-out, but it is seriously complex, and it requires a lot of thought. For form state, you can go with useState as a beginner on a small project or Formik for a medium one. If you are working in a large team, I am sure the tech lead will be the decision-maker on this. If you are an intermediate or an experienced developer, state machines are pretty nice to use.
For UI state, you can use useState, useContext + useReducer for an entry-level small to medium project. For an intermediate developer, you can either go with React Toolkit or useContext + useReducer for small to medium projects. For an advanced developer, you can go with useContext + useReducer, Jotai, Valtio, and Recoil for medium to large projects. If you are using GraohQL, Relay is also a good one to consider.
For server cache state, SWR or React Query are both really good solutions.
All in all, use what feels right to you and one that you can manage properly depending on the level you are at. Some of these state management libraries take a while to learn and apply. Take your time. One step at a time. I hope this article has given you some useful insights or has helped you narrow down your path to learning state management.
More content at plainenglish.io
I am a full-stack web developer. I love sharing my knowledge of web development technologies and programming in general.