Redux vs Recoil: which should you use?

Updated 22 November 2020
·
react
redux
recoil

Recoil, Facebook's experimental state management library for React, has been receiving a lot of hype. With Redux being the most popular state management library, it's natural to ask the question - when does it make sense to use one over the other?

Let's take a look at some API differences between the two, plus some potential performance wins for Recoil.

Redux Stores vs Recoil Atoms

In Redux, we have the concept of a centralised store where all the data for our app lives.

In a simple to-do list example, your store would look something like this:

{
    listName: 'My cool to-do list',
    tasks: {
        '1': {
            name: 'My first task',
        }
    }
}

Recoil instead splits your state into individual atoms. You would have one atom for listName, and another for tasks. Here’s how you would create the atom for your tasks (from the Recoil docs):

const todoListState = atom({
    key: 'todoListState',
    default: [],
});

At ReactEurope, the creator of Recoil, Dave McCabe, also introduced the possibility of creating individual atoms for each of your items:

export const itemWithId =
    memoize(id => atom({
        key: `item${id}`,
        default: {...},
    }));

This means we could break it down further, and create an individual atom for each task.

Redux vs Recoil: Performance

McCabe mentioned in a comment on HackerNews:

"Well, I know that on one tool we saw a 20x or so speedup compared to using Redux. This is because Redux is O(n) in that it has to ask each connected component whether it needs to re-render, whereas we can be O(1)."

Recoil is O(1) because when you edit the data in an atom, only components subscribed to that atom will re-render.

In our to-do list example, if we had 20 separate tasks, we would have 20 separate atoms. If we edited the fifth task, only the fifth task would need to re-render.

Redux does allow you to achieve the same effect using selectors:

// selector
const taskSelector = (id) => state.tasks[id];

// component code
const task = useSelector(taskSelector(id));

With Redux, the component will only need to re-render when the specific task re-renders - same as Recoil.

The caveat here is that each time any part of the state changes, our taskSelector would have to re-calculate.

We can use optimisations like createSelector to reduce expensive calculations. However it will still need to check whether it needs to re-calculate.

With these performance optimisations, Recoil makes sense for huge apps that need to render a lot of components. A regular app is probably going to perform just fine with Redux. I don’t think it’s worth switching to Recoil for the potential (maybe nonexistent) performance benefits.

Redux vs Recoil vs Context

You might also be wondering - what about React Context? Do you need a state management library at all?

Using React Context does have some potential downsides when it comes to performance. Any changes to state will cause re-renders for components that are using that state.

I have a separate post on React Context that explains this issue, and provides a workaround library you can use called React Tracked.

Redux vs Recoil: API

So performance might not be the reason you need to switch to Recoil, but we can also compare the API of both libraries.

I'll be comparing Redux Toolkit with Recoil, to create a simple example that lets you change the value stored in a text box.

💡 What is Redux Toolkit?

Redux Toolkit is a helper library aimed to help you simplify how you use Redux. If you're interested in learning more, I have a separate post on Getting started with Redux Toolkit.

Redux state management example

Recoil state management example

I also have to-do list examples of both Redux Toolkit and Recoil and other libraries over at react-state-comparison.

Redux vs Recoil: API differences

Comparing the differences in API between Redux and Recoil:

  • Both libraries need you to wrap the entire app in a provider component
  • Recoil lets you initialise a store using atom(). This also comes with a selector and dispatchable action built-in
  • Redux lets you create a store using createSlice() together with configureStore(). This comes with actions and a reducer. You'll need to define a selector separately
  • Redux also requires the useDispatch and useSelector hooks to be able to dispatch actions and use selectors

Redux definitely does have some additional boilerplate to get started!

Conclusion

To summarise some of the differences between Redux and Recoil:

  • Even with Redux Toolkit, Redux is still more "boilerplatey" than Recoil
  • Recoil may provide performance benefits, but only if your app is complex enough
  • Recoil is still in an experimental phase, while Redux is an established library

Another thing to point out is that Redux provides middleware functionality, while Recoil does not yet support it.

Recoil does have some nice-looking benefits - it is possibly more performant, and has slightly less boilerplate to get started. However, it is still worth keeping in mind that it is an experimental library. So there are some risks you will be taking on if you choose to use it for a large project.

Comments