DEV Community

Cover image for State Management Battle in React 2021: Hooks, Redux, and Recoil
Mihaela for WorksHub

Posted on • Updated on

State Management Battle in React 2021: Hooks, Redux, and Recoil

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.

Redux workflow

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()
  }
}, []);
Enter fullscreen mode Exit fullscreen mode

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>
)
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

We put the root component between the myContext Provider:

function App() {
    return (
        <myContext.Provider value={900}>
            <Root />
        </myContext.Provider>
    )
}
Enter fullscreen mode Exit fullscreen mode

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>
</>
)
}
Enter fullscreen mode Exit fullscreen mode




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

Article by Blessing Krofegha, originally published on JavaScript Works.

Latest comments (41)

Collapse
 
mprzodala profile image
Mariusz

Try eventrix. It’s a new open source alternative for redux. It’s faster than redux and it has a built-in event bus. Test it and let me know what you think.

Collapse
 
liximomo profile image
liximomo
Collapse
 
hknasit profile image
Harsh Nasit

should I use React Hooks or State Management Library or I should create my JavaScript code which holds the global state?

Collapse
 
eduardodevop profile image
eduardodevop

This is simply... Just use "Easy Peasy" library, this is zero boilerplate, easy and powerful 🥰

Collapse
 
ahmedmesawer profile image
Ahmed Mesawer

What about reactn??!!

Collapse
 
phacus profile image
Tiago Rinaldi

Thanks for sharing sources.

Unfortunately the comments section is not that capable to discuss your article. (No, I would not do a better job commenting too)

Collapse
 
fhefh2015 profile image
fhefh2015

React Query + Zustand

Collapse
 
redbar0n profile image
Magne • Edited

Valtio is the newest kid on the block. From the creator of Zustand and Jotai.

  • Pros:

  • Cons:

    • mutation based (no immutability...)
    • a bit magical
    • can have some performance overhead ref

Would be interesting to hear your opinion on Valtio.

For reference, here is a useful brief overview over the general approaches:

  • Flux (Redux, Zustand)
  • Proxy (Mobx, Valtio)
  • Atomic (Recoil, Jotai)
Collapse
 
yeo311 profile image
Jihoon Yeo

This is so helpful post for me. I appreciate you. Can I translate this into Korean and repost on my blog? I think this is helpful to any other Korean developers.

Collapse
 
mihaelapopa profile image
Mihaela

Sure, please feel free to do it!

Collapse
 
avkonst profile image
Andrey • Edited

Hookstate hookstate.js.org/ should be in the list too. It is the only lib which gives an efficient solution for deep nested state updates. And it probably has the simplest same API for all states: global, local, parent, nested, async, etc..

Collapse
 
robvirtuoso profile image
robvirtuoso

Why do you feel the need to promote your library everywhere if it is really that good?

Collapse
 
avkonst profile image
Andrey

Everywhere is a bold statement. I do not have a budget to do it everywhere.

Thread Thread
 
robvirtuoso profile image
robvirtuoso

That is exactly the point... if you had the budget, you would be literally everywhere.

Thread Thread
 
avkonst profile image
Andrey

No, I would not. I direct money elsewhere. This library is published without having a viable commercial business model in mind. I have got other things for this purpose.

Collapse
 
juriii profile image
Jurij Koch

what is recommended for classic apps with one save button/reset button for multiple tabs and many textfields/tables?

Collapse
 
dcsan profile image
dc • Edited

What about loading data from the server, the main point of state, and ridiculous redux doesn't handle it without thunks or yet more plugins. How is recoil?

Collapse
 
elusive241 profile image
elusive24

Redux is unopinionated library, thats why it doesnt include server data handling.
Actually last alpha of redux-toolkit contains RTK Query - tool for data fetching and caching.
github.com/rtk-incubator/rtk-query

Collapse
 
rel555 profile image
rel555

In my opinion Apollo client (GraphQL) is the best

Collapse
 
onlyayep profile image
Arif Rahman

Being experience using Apollo Client for couple of years. I couldn’t agree more. Though is quite a different approach on managing state with using normalizing cache.

Apollo useVar really being helpful for managing client state!

Collapse
 
cadams profile image
Chad Adams

React Query + Zustand 🙂

Collapse
 
csulit profile image
Christian Angelo M Sulit • Edited

Any benefits when paired with zustand? Why not use getQuerData instead? I want to hear your thoughts about this pair 😊 -just curious

Collapse
 
sefrem profile image
sefrem

This article could've consisted only of one word - Mobx.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.