William Hill is a senior software engineer at Meroxa with experience as a full-stack engineer and a DevOps engineer. His background includes creating apps for subject matter experts in nonproliferation, climate science, and basketball analytics. William is a proponent of increasing diversity in the computing field by recruiting and mentoring engineers from underrepresented groups.
Let’s say you want to check the score of a game on ESPN. You click on the box score expecting to see the results for that particular game. However, the data that is being displayed is from a different game entirely.
The mismatch between the queried data and the data displayed on the page could be due to improper state management, which prevents your React app from syncing state across multiple components. The more your app grows, the more challenging it becomes to manage global state effectively.
There are several strategies for managing global state within React. However, not all choices are created equal for every use case. Selecting a global state management strategy is an important yet challenging part of building your app. Whether you are working on a greenfield project or an app refactor, you’ll need to weigh tradeoffs regarding complexity, testing, and tooling.
Today we’ll focus on the pros and cons of three popular global state management strategies for React developers:
React Context
Redux
Recoil
In the words of Black Sheep, you can get with this, or you can get with that. The choice is yours. This article will equip you with the knowledge to make that choice.
We’ll cover:
React Development for Professionals
Are you ready to take your React skills to the next level? This module is designed for developers who want to improve their React and development skills. You will learn how to use React Hooks as opposed to using class components, which will help you achieve better code composition, code reusability, and unit testing. This path will also help you to use design patterns and think more abstractly about how you create applications in React. We will also cover the latest features introduced in React as well as state management In details using Redux in dedicated modules. Finally, we will cover Firebase, which makes it easier to develop and deploy enterprise level React applications.
React Context is the batteries included approach to global state management. It doesn’t require the use of any external dependencies since it is part of the native React API.
Sharing state amongst components and the React application is simple:
Define the Context object.
Wrap the component tree with the Context Provider.
The result: everything nested under that context can access the data within it.
While React’s Context API can be a good choice for fairly simple apps, it has fewer optimizations for managing complex pieces of data.
Context can only store a single value, as opposed to an indefinite set of values (each of which has its own consumers). Passing an object to a Context provides some flexibility for the single value constraint. However, that may not solve your cases as your state management grows in complexity.
It’s possible to define multiple Contexts that wrap a component tree, but defining and consuming multiple Contexts can quickly become messy. In addition, you could run into performance issues as your app grows because Context can cause frequent and unnecessary re-renders as all components in the tree get re-rendered on state changes.
When used properly, React’s Context API is a powerful state management solution, despite its limits when dealing with a high frequency of state changes.
React Context Pros
Built-in strategy that doesn’t require external dependencies
Effective for relatively small apps
React Context Cons
Redux is an open-source JavaScript library that can integrate with any UI framework. Despite not being designed specifically for React, Redux is one of the most popular React state management solutions. That’s because Redux is highly effective at managing complexity.
Redux stores the entire global state of an app in an object tree inside a single store. Actions that happen within the app can get dispatched to the store. From there, a reducer function derives the new application state using the previous state and the action that was dispatched, as shown in this figure of a React-Redux application flow:
I think of Redux as the kitchen sink approach. As your React app grows to require more complex state manipulations and management, Redux can handle all of that far more effectively than React Context. At the same time, Redux adds a layer of complexity to your codebase and code maintenance. This can mean a steep learning curve for developers.
Fortunately, Redux has a large community and ample resources to support developer learning. The Redux Toolkit can help you navigate many of the challenges you’ll encounter by providing an opinionated set of best practices.
The question is whether that learning curve is worthwhile for your use case. Do you need the capacity to manage complex pieces of data? For relatively small and simple apps, the Redux library can be overkill. For large enterprise apps, you may want to invest the extra time in learning and implementing Redux.
Redux Pros
Effective at complex React state management
Has useful dev tools for development and debugging
Has option for server-side rendering
Redux Cons
Can have a significant learning curve
Initial setup can be challenging (although Redux Toolkit helps make it easier)
Can have more boilerplate code than other approaches
Recoil is an experimental state management library developed by Meta (the same people who developed React). As a result, the Recoil API and coding style mesh really well with core React.
Recoil lets you create a data-flow graph that flows from atoms (shared state) through selectors (pure functions) and down into your React components. Selectors transform this state either synchronously or asynchronously. The state definition is incremental and distributed, making code-splitting possible.
As an ongoing Meta experiment, Recoil is on the cutting edge of global state management solutions. However, its experimental nature can also be a downside. Developers are constantly incorporating feedback and adjusting the Recoil methods and APIs. This means that users can experience breaking changes. On top of that, there are limited educational resources to support Recoil users.
Recoil Pros
Has a similar API to React local state
Helpful if you have a large number of interdependent components
Recoil Cons
Unstable API due to being experimental
Does not support middleware
A previous company I worked at used Redux as the state management strategy. I experienced that aforementioned learning curve firsthand. While I got up to speed on Redux, we were also engaged in high-level conversations about global state management. Should we take a different approach?
We started by assessing why we used Redux in the first place. Part of the reason is we were on the older spec of React. As a result, we didn’t have access to React hooks, which allow you to use state and other React features without writing classes.
Things changed once we updated our React version and gained access to React hooks. This opened up options beyond Redux. Our team discussed the tradeoffs of the strategies we’ve covered today.
This could work for a few isolated parts of our React application. But overall, our enterprise-level app is too complex. It will be more trouble than it’s worth to wrap our component tree in multiple Contexts.
This option is appealing because it’s on the bleeding edge and well supported by Meta. However, the fact remains that Recoil is very new, so there are frequent changes to the library that can crash our app. Is the innovation behind Recoil worth the inevitable breaking changes?
Orienting to how we use Redux is a heavy lift. However, this strategy is reliable and enables us to manage multiple states effectively. Now that we have the option of a more hooks-based approach, we may explore moving away from Redux to simplify our code base. But for now, it makes sense to focus on how we can use Redux more intelligently.
A key takeaway from this process is that choosing the right global state management strategy starts with understanding your use case. Ask yourself:
What is the complexity of your application?
How might this change in the future?
There is no one-size-fits-all solution to managing global state. In addition, the strategy that works today might not be the best choice six months from now.
You can get with this, or you can get with that. The choice is yours. The tradeoffs we’ve discussed here, grounded in deep knowledge of your use case, will help you to make an informed one.
Free Resources