How to manage your global state in a React app?
What options do I have to manage my React app state? What are the different state management libraries available?
Data management is very hard. And as an application grows and evolves, it’s essential to have a strategy in place to manage data. Whatever state management library is used, a robust application well-architected and thoroughly designed can make a massive difference in the developers’ productivity.
Thanks to the development of React and new libraries, Redux isn’t the only choice anymore. New libraries and technologies are available to simplify the way of managing data and application states.
Where should I store my data?
It’s imperative to understand what kind of data needs to be stored and where it should live. As a developer, the question to ask ourselves is: Does my entire application need to know about this?
Once we know the type of data we have, we can decide how best to manage it. Let’s look at different types of data and states we commonly have in a React application and how you should deal with those.
Disclaimer: this list is not exhaustive. There are other types of data not mentioned and other libraries that could be considered. I have decided to list what I believe is easy to use and can work in most cases.
Server state or API data
That’s probably the primary type of data most developers have to deal with. Fetching and updating server data happens all the time. To cache the data at the front-end application level, we want to store this at a global level. While some data might not be used by the entire app, separating the data layer is desired to simplify any application's growth and evolution.
My personal preference for storing API data is React-Query. It’s a great library that resolves asynchronous data fetching, frequent updating, offline caching, and synchronization with the APIs. You can read this noteworthy article talking about how React-Query is sometimes the only library needed, especially for API cached data.
You can find a comparison with other global data managing libraries here, looking at different points and key features to help you decide which one is more relevant for a project.
Form data or user entry data
Storing form data is pretty standard for many applications. A form is usually a small part of an app that collects user data. However, there is often no reason to share this data at the global level of any application. Keeping this data at the local level makes more sense, even if the form goes through multiple screens.
One library to look at is react-hook-form, which provides form state management and validation solution. It is easy to use and performs really well compared to some other form libraries. Formik is also an excellent library.
For relatively simple form or data entry, using React Context directly can also be an option. The data would then be stored in a React state and propagated via the Context consumer.
Navigation-related states, such as knowing the screen currently visible, or the parameters available, are something an application might need to access from anywhere. However, managing this type of data ourselves can be complicated and probably not a good idea.
There are a few libraries available to manage navigation. For example, React Router for web apps or React Navigation for mobile apps with React-Native. Those libraries provide everything needed in terms of data and orchestrate the navigation between screens.
There are times when it’s needed to share a component state with another component. For example, a music player that shares the “Play” state with different components to display information or create animations.
One simple option is to have a React state on a top-level component and pass down the state via props. This is called prop drilling. If the number of nested components is large, React Context is probably one of the best options. The state is set at a top-level state then passed down via the Context. Recoil can also be a good library to consider. It’s an atomic state management library that makes sharing small states between components easy. Zustand can also be a good option for this.
Modal view or similar types of global components state
When some components are visible globally, such as a global modal you can trigger and show from anywhere in your app, then it makes sense to store its state globally.
Similarly, when it is needed to share a state between 2 components that are very “far” from each other, React Context with a Provider set at the very top level of the application can be the solution. Recoil or even Zustand can be alternatives as well. And Redux, in combination with Redux Toolkit, can be an option if you really want to, as long as the data is serializable and there is no need to pass callback functions.
User preferences or user session data
Store locally or globally
When there is data associated with the current user, such as an access token, or storing the app dark mode state, in most cases those need to be stored globally. But there could be some cases where only a subset of the app needs to store and access user data.
On the web, it’s common to store those in a cookie or the local storage. So the data is not lost when the user refreshes the page. However, on a mobile app using React-Native, this type of data is better stored on some sort of state management which performs better than using the AsyncStorage or any type of local database.
Simply using React Context at the top level to store this type of data is probably enough. Redux could also be an option if you have complex states to manage.
Configs and feature flags
However, if your config changes during the lifecycle of your application and requires you to make additional API calls to retrieve the changes, you might want to consider using one of the libraries mentioned for the Server state above.
What about just Redux?
Redux is the leading state management library for React applications. That is a fact. It gets downloaded many million times every month, and over 50% of all React projects use it. It has been adopted by most React developers for small and large-scale React projects on both the web and mobile apps with React-Native.
But is Redux still the right choice for storing data? Maybe not anymore.
Redux is often brought to a project because it solves the problem of passing data to any component. It was the primary option available for many years. Developers used it as a silver bullet for all the data problems. But in some cases, you face an enormous data store that is difficult to manage and often degrade application performance as too many actions get dispatched simultaneously, updating the store and causing more re-renders.
“People often choose Redux before they need it.”
Dan Abramov, co-author of Redux.
With the introduction of new tools such as Context and hooks in the React ecosystem, more and more developers will consider Redux as their last option. And its actual usage in projects is becoming a factor in hiring and keeping developers.
Developers lost interest in it, primarily due to its verbosity, the difficulties to type with Typescript and because managing a lot of data manually is hard. It is less relevant today than it used to be and despite new tools and utilities recently built around Redux. Developers are moving to other state management libraries to help them resolve their problems in different ways.
Nevertheless, this is a controversial topic. There are still a majority of developers that wouldn’t even consider other state management options.
What does the co-author of Redux think today?
A video was recently published where Dan Abramov, the co-author of the initial version of Redux, was asked to explain when it can be a good idea to use Redux in a React project. For context, Dan Abramov works today for Facebook and is a leading contributor to the React library. He doesn’t work on Redux anymore.
You can watch the full video on YouTube.
When the project already uses it
If you work on a project and Redux is already in use, and the developers working on the project already know how to use it, then there is no actual reason to stop using Redux. There is no need to remove it altogether, and Redux can be helpful. If it works fine for the team and has never been a problem, then it’s okay to keep it.
When you don’t know where to store the data
If you have two components that need to share the same piece of state, it could be an option to consider. Well, even then, it might not be the best idea, according to him. Dan considers it might be worth looking at other places to put data. For example, storing a state at the top level and sharing this state using a Context rather than storing the data on Redux.
Consider other libraries to store data
It is pretty much the advice given by Dan. He advises looking at other libraries such as Relay, React-Query, or Apollo, especially to cache API data.
Do I still need Redux?
When you stop using Redux for managing your API data, it makes your Redux store so much lighter, and it can feel overwhelming to use it for the other types of data or states. But again, that really depends on the data that needs to be managed. Redux does resolve a problem of global state management, and if that works fine in your project, then there is no reason to replace it with anything else. But if you feel it has been painful to manage data and states in your application and it is slowing down the development team, then it can be a good idea to consider alternatives.
Each library has its purpose and different ways of working. Some resolve state management in a very easy way for small amounts of data, while others are more advanced and provide excellent tools to simplify your day-to-day development.
I usually go with React-Query to manage and cache API data, react-hook-form for user entry and I try to stick with Context for the rest when possible. I might also consider Recoil for sharing very small states between parts of my application.
Those tools working together have allowed me to manage all my application states without Redux, even on very large-scale applications with hundreds of screens. They tend to be easier to use and learn than Redux and they resolve data and state problems very concisely.
Hi, I’m Alexis! I’m a Mobile Engineer focused on supervising and building applications with React-Native and Typescript. I specialize in solution architecture for large-scale applications, and I enjoy spending countless hours learning and playing with new technologies to use in my next projects.
You can follow me on Twitter: @alexmngn