State Management Battle in React 2021: Hooks, Redux, and Recoil
Mihaela
Posted on April 13, 2021
Introduction:
Over the years, the massive growth of React.JS has given birth to different state management libraries, amongst other things.
The state management libraries available in React at our disposal at the time of this article are enormous. Therefore, knowing what state management library to choose for a particular project not to get carried away by the noise and news from the React community is a significant factor in facilitating the development of an application.
Some developers tackle the challenge by using React Hooks; others combine them with application state management libraries like Redux or the newly release Recoil.
In this article, we will discuss state management using Redux, Hooks, and Recoil in a typical React application and their best uses cases.
We will also try to answer questions like:
- What metric to consider before choosing a state management library?
Note: This tutorial will be beneficial to readers interested in developing React application that requires a state management library.
This article isn’t an intro to state management in React. It requires a basic understanding of React, hooks, and a bit of Redux; hence, if you’re starting with React and state management in React, please go through these basics before beginning this tutorial 😎.
What’s State in a Nutshell?
State management is simply a way to engender communication and sharing of data across components. It creates a concrete data structure to represent your app's State that you can read and write.
Since React 16.8, every React component, whether functional or class, can have a state.
In the simplest definition, State is a JavaScript object that represents the part of a component that can change based on a resultant action of a user. You could also say states are simply the memory of a component.
When a user performs an action in a typical React app, changes occur in the component's state. While this isn't bad, it quickly becomes a problem if the app begins to scale; hence, such an app's complexity makes it extremely difficult to keep track of all dependencies.
To answer the introduction question, suppose we are building an eCommerce application; in an app like this, just about every element can be a component – the shopping cart, the buttons, the view cart session, checkout, login bar, etc. In this app, just a single user action of adding to the cart can influence many other components by:
- changing the State of the cart component itself,
- adding the cart to the user's cart history,
- checkout product items.
And that's to mention only a few from the other plenty stuff that we could add to the eCommerce app. If the engineers in charge do not consider scalability while developing the app, they might soon quickly run into many bugs and problems in the long run.
Constantly debugging and revamping an app like this could eventually be a pain.
The above scenarios show us the importance of the state in a typical React application.
In managing the state in this application, we could use any library of our choice; they would still get the job done regardless.
Usually, the state will have to be lifted to the closest parent component and the next until it gets to an ancestor common to both components that need the state, and then it is passed down. This process can be overwhelming and makes the state challenging to maintain. Often it might warrant you to pass data to components that do not even need it.
State management gets messy as the app grows bigger. That is why you need a state management tool like Redux, Recoil, making it easier to maintain these states.
In the following sections, we would practically look at all the state management libraries(Redux, Hooks, Recoil), their uniqueness, and what to consider before going for any of them.
Redux
The first on our list is Redux; It has been around for a while, pretty much the first react-based state management library.
The state management library Redux was created to address the problem in our eCommerce app. It provides a JavaScript object called the store, which, once set up, includes all states in your application and updates them when necessary. Here is a simplified visualization of how Redux works.
Perhaps you're asking, why is Redux often used with React? The reason from my experiences is because, Redux handles state updates in response to user's actions, especially in UI; Asides from that, Redux can be used as standalone state management from any framework.
When to use Redux?
Redux is one of the most popular React state management libraries as of the time of this article.
In this section, we would look closely into when to use Redux in an application.
Firstly, Redux allows you to manage your app's state in a single place and keep changes in your app more predictable and traceable. It makes occurring changes in your app easier to figure out. Unfortunately, all of these benefits come with specific constraints and tradeoffs.
Frequently, developers feel using Redux adds up some boilerplate code, making little things seemingly overwhelming; however, that depends solely on the app's architectural decision.
One of the easiest ways to know when you genuinely need to use Redux is when managing state locally begins to look messy.
As the application grows, so does state sharing across components gets tedious.
At that point, you'd now start looking for ways to make the process hassle-free.
In the next section, we would look at why we should Redux with React.
Why use Redux?
Using Redux with React takes away the hassle of lifting upstate, making it easier for you to trace which action causes any change, hence simplifying the app and making it easier to maintain.
Let's take a look at some tradeoffs that come with using Redux for state management.
Community Support
As the official binding library for React and Redux, React-Redux encompasses a large community of users. that makes it easier to ask for help, learn about best practices, use libraries that build on React-Redux, and reuse your knowledge across different applications.
It's the highest stared React state management library on Github.
Enhances Performance
React Redux assures performance optimization so that only the connected component only re-renders when it needs to; hence keeping the app's state global wouldn't result in any problem.
Redux makes the state predictable
In Redux, the state is always predictable. If the same state and action move to a reducer, it will obtain the same result because reducers are pure functions. The state is also immutable and is never changed. It makes it possible to implement arduous tasks like infinite undo and redo. It is also possible to implement time travel — that is, the ability to move back and forth among the previous states and view the results in real-time.
State persistence on Local Storage
Persisting some of the app’s state on local storage and restoring it after a refresh is possible. It makes storing things like cart data on local storage really awesome.
Server-side rendering
We can also use redux for server-side rendering. With it, you can handle the app's initial render by sending the state of an app to the server along with its response to the server request.
Redux is maintainable
Redux is strict about how code should get designed, making it easier for someone abreast with Redux to understand any Redux application structure. It generally makes it easier to maintain. It also helps you segregate your business logic from your component tree. For large-scale apps, it's critical to keep your app more predictable and maintainable.
Debugging is made easy
Redux makes it easy to debug an application. By logging actions and state, it is easy to understand coding errors, network errors, and other forms of bugs that might come up during production.
Besides logging, it has excellent DevTools that allows you to time-travel actions, persists actions on page refresh, etc. For medium- and large-scale apps, debugging takes more time than actually developing features.
While Redux has its benefits, it doesn't warrant that you add Redux in all your apps.
Your application can work well without Redux.
Recoil
Recoil seems to be the newest tool on the state management community— A community with tons of excellent libraries like Context, Mobx, and Redux, etc.
Before going into details about Recoil, I'd like to point out that this new state management library is not the "official" state management library for React.
However, the record shows that it was built and released by engineers from Facebook's team, the React creator.
But then, just as Redux isn't an official state management library for React, Recoil isn't either but may gain mass adoption by React enthusiasts if it proves valuable to the React ecosystem at large.
The primary problem Recoil solves
While it has its learning curve, it still solves the same as most other state management libraries: global state management.
After using Recoil for only a short while, here are the distinctions I think Recoils comes very handy.
React-like approach and simplicity
The simplicity of Recoil is second to none, hence the reason it's on this list.
You could build whatever app you build with Recoil as you could make just as with Redux or MobX.
However, Recoil feels like using a global version of React's useState. It also supports Concurrent Mode, a huge plus (this is still in the works at the time of writing).
Easy Learning Curve
Recoil doesn't impose a strict learning curve as Redux and Mobx do.
They aren't so much to learn asides from Atom and Selectors, which are easy to understand.
App-wide observation
Similar to other state management libraries, Recoil handles app-wide state observations well. Other benefits of using Recoil includes;
- Boilerplate-free API
- Distributed and incremental state definition
Recoil's central core concepts are Atoms and Selectors; covering this section is beyond the scope of this article. However, you can check their documentation for an in-depth overview.
When to use Recoil
In less than two years of its release, Recoil has grown so much that it has about 12k plus stars on Github at the time of this article. Asides from that, it's gradually gaining momentum and mass adoption amongst React enthusiasts and the React community at large.
Personally speaking, the only reason I have used Recoil in any of my projects is when I don't intend to have so much Redux boilerplate in my codebase. I have used Recoil on production once, and nothing terrible has happened; everything still works very well to date.
So when to use Recoil might solely depend on your app's architecture decision, and if you are a lover of simplicity like myself, you might jump into using Recoil 😎.
Using React Hooks
Hooks is one of the most outstanding features ever added to the React library since its creation. Hooks brought ‘state’ to functional components. Now, functional components can create and manage local states on their own, just like class components.
Anyone already into React should get familiar with React hooks, including useState
, useEffect
, and useReducer
, etc.
This section will discuss how handy React Hooks can be standalone without intermeddling with any external state management library.
You could use React Hooks as your primary state management tool without any library, but this will depend on your experience and understanding of React hooks.
They are powerful on their own and can accomplish almost anything an external library could do.
To some extent, other state management tools have a few advantages. Still, their procedures make it challenging to get started. Like in the case of Redux, some boilerplate code is needed to get it working in our application; hence, it introduces unnecessary complexity.
On the other hand, with the useContext
API and React Hooks, there is no need to install external libraries to get our app working. It makes it a much simpler, more straightforward way to handle global state management in React applications.
Note: Assuming you’re already familiar with useState
, we would look into two hooks that aid the process of state management in React.
The useReducer
Hook
The useReducer
Hook came with React 16.8. Just like the reduce()
method in JavaScript, the useReducer
Hook receives two values as its argument — a reducer function and an initial state — and then returns a new state:
const [state, dispatch] = useReducer((state, action) => {
const { type } = action;
switch(action) {
case 'action description':
const newState = // do something with the action
return newState;
default:
throw new Error()
}
}, []);
In the snippet above, we’ve defined our state and a corresponding method, dispatch
, handling it. When we call the dispatch
method, the useReducer()
Hook will perform an action based on the type
that our method receives in its action argument:
...
return (
<button onClick={() =>
dispatch({ type: 'action type'})}>
</button>
)
useContext
This hook is used to get the current context of a Provider. To create and provide a context, we use the React.createContext
API.
const myContext = React.createContext()
We put the root component between the myContext
Provider:
function App() {
return (
<myContext.Provider value={900}>
<Root />
</myContext.Provider>
)
}
To consume the value provided by the <myContext.Provider></myContext.Provider>
we use the useContext
hook.
function Root() {
const value = useContext(myContext)
return (
<>
<h3>My Context value: {value} </h3>
</>
)
}
Using useReducer and useContext
Using useContext together with useReducer takes the component co-located state management on another level. Suddenly we can pass the state container created by useReducer and its dispatch function to any component from any top-level component. It can also be the most top-level component to make the state "global." It's also possible to pass things down only using React props, but React's Context API makes your state and dispatch function available anywhere without explicitly passing everything down the component tree.
Conclusion
In this article, we tried to cover the most trending state management tools for React in 2021, how they play an essential role in React state management, and when to use them in a project.
I'd like to know what your experiences are in managing state in a typical React application.
Resources
- When (and when not) to use Redux - Christian Nwamba
- React State Hooks: useReducer, useState, useContext - Robin Weiruch
- Recoil in action: Building a reusable code block component - Tomi Odunsanya
- Refactoring a Redux app to use Recoil - Ohans Emmanuel
- Why React projects still use Redux - Alexandru-Dan Pop
Article by Blessing Krofegha, originally published on JavaScript Works.
Posted on April 13, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.