DEV Community

sawincp
sawincp

Posted on

Taming the State Beast: Redux vs. Recoil in React

React's beauty lies in its component-driven nature, but managing state across those components can become messy. That's where state management libraries like Redux and Recoil come in, wrangling your app's data into a smooth, predictable flow. But which one deserves a place in your toolkit? Let's break down their key differences:

Architecture

  1. Redux: Think of Redux as a central bank for your app's state. Everything goes in and out through a single source of truth, the store. Actions, sent by components, trigger reducers that update the store, and changes automatically ripple through the app.

  2. Recoil: Imagine a network of smaller vaults, called atoms, scattered across your components. They each hold independent pieces of state, connected by selectors that can combine or transform data between them. Updates happen locally, with only affected components notified.

Learning Curve

  1. Redux: Buckle up for a steeper climb. Redux has a well-defined structure and requires more boilerplate code, but its predictability and extensive community support make it a reliable choice for complex projects.

  2. Recoil: Take a gentler slope. Recoil boasts a simpler API and less code, making it easier to jump in. However, its decentralized nature can feel less structured and lacks the same level of community resources as its established counterpart.

How it works: Redux

At its core, Redux allows us to create a single source of truth for our application. Think of this as a vault which holds the entire application's state which is called a store.

To set up a store you have to set up a pure function called a Reducer that takes in the current state and an action that returns the updated state.

function counterReducer(state = 0, action) {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
}
Enter fullscreen mode Exit fullscreen mode

To store your initial values and you need to import the createStore function from the Redux library.

import { createStore } from 'redux';

const initialState = {
  counter: 0,
};
Enter fullscreen mode Exit fullscreen mode

From here you can create your store (vault) with your reducer and initial state as arguments...

const store = createStore(counterReducer, initialState);

This will create the store object that will hold your app's state and manage its updates.

How to use Store from Redux

In order to use your newly created store you first need to wrap your root component with the Provider component and pass the store as a prop. This allows the store to be accessible to all components in your app.

import React from 'react';
import { Provider } from 'react-redux';
import store from './store'; // Import your created store

function App() {
  return (
    <Provider store={store}>
      {/* Your app components here */}
    </Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

From here we can connect two hooks to the store in our components.

1) Use the useSelector hook to read data from the store into your component.

2) Use the useDispatch hook to dispatch actions that trigger state updates in your components.

import { useSelector, useDispatch } from 'react-redux';

function CounterComponent() {
  const count = useSelector((state) => state.counter);
  const dispatch = useDispatch();

  const handleIncrement = () => {
    dispatch({ type: 'INCREMENT' });
  };

  return (
    <div>
      Count: {count}
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In our example our useSelector retrieves the data from our Redux store by accessing the counter value and storing it in the count variable. It allows the component to display the current count and react to changes in the store.

useDispatch will dispatch our action to trigger state updates in the store. The useDispatch hook returns a function called dispatch that's used to send actions to the Redux store. When our handleIncrement function is called, it dispatches an action with the type "INCREMENT" to our Redux store where our Reducer receives that action and performs the necessary logic and stores the new state value in our counter variable.

PHEWWWWW.... that's a lot to take in. Let's take a look at our other example.

How it works: Recoil

One of the biggest differences between Recoil and Redux is how they approach state.

Redux uses a single store to hold the entire app's state where components read and update the store through actions and reducers (Centralized).

Recoil uses atoms scattered across components, each holding a specific piece of state where components can directly access and update these atoms (Decentralized).

To use Recoil we first have to import and define an atom from our Recoil library.

import { atom } from 'recoil';

const counterState = atom({
  key: 'counter',
  default: 0,
});
Enter fullscreen mode Exit fullscreen mode

Similar to Redux, we then have to make Recoil available throughout our app by wrapping our root component with RecoilRoot.

import React from 'react';
import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot>
      {/* Your app components here */}
    </RecoilRoot>
  );
}
Enter fullscreen mode Exit fullscreen mode

From here we can use Recoil hooks just like our useState hooks from our React library. We use useRecoilState in our components to read and update our state in the component.

import { useRecoilState } from 'recoil';

function CounterComponent() {
  const [count, setCount] = useRecoilState(counterState);

  const handleIncrement = () => {
    setCount(count + 1);
  };

  return (
    <div>
      Count: {count}
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Like our useState hook, when a user clicks on our button and triggers our handleIncrement function, the function process the request by updating the setState variable which directly updates the value of our counterState atom.

Seems a little bit easier eh?

Conclusion

Both of these state management libraries are powerful tools to help tame state in your application. While Redux has a predictable data flow and a centralized store, it also comes with a steeper learning curve and less flexible architecture. Recoil on the other hand provides a simple API with flexible and decentralized state management but is considered less mature with a smaller community than Redux.

Ultimately, the best choice for your project depends on your project needs and preferences:

Choose Redux if you have a large and complex application requiring predictable state management, strong community, and established best practices

OR

Choose Recoil if you prefer a simpler API, have smaller projects, and prioritize potential performance benefits.

Choose the tool that's right for you!

Top comments (0)