How I Dropped Redux for the Context API
The personal story of how I replaced Redux with Context API
React 16 introduced a new Context API to replace the deprecated one. OK, it’s been more than a year since the release of version 16.3, but it still seems fresh in the React ecosystem.
This new API came with the promise to solve a lot of problems with the previous experimental way to use contexts. To me, it did a lot more; it changed the way I make React applications. This is the story of how I managed it.
I won’t give a course on how Redux works. If you want a refresher, you can check the amazing course from Dan Abramov on Egghead. Plus, you’ll eventually remove Redux from your apps, so do we need a full course on it?
OK, let’s say we have an app that tells if I’m available for a beer. It consists of the following:
In my sample code, I created four files to handle the parts of a Redux application:
actions/beer.js: A file that contains a constant for every action in my app. This could be inlined directly in the other files, but I like to keep things clear and concerns separated.
dispatchers/beer.js: The home of every action my Redux model has. In this case, I only have one
toogleBeerAvailabilitymethod, which dispatches the action from the previous file.
reducers/beer.js: The storage engine of my Redux model, which changes the value of my availability if the
TOGGLE_AVAILABILITY_FOR_BEERdispatcher is called.
components/beer.jsx: The component that shows and toggles my availability. We use
react-reduxto map the redux properties to my component props.
That’s a lot of code, but it’s necessary for a robust system with Redux. Now, we’re going to drop Redux with the same result. But first, why do we want to drop Redux?
I made that move simply to reduce weight in my application by removing two dependencies:
react-redux. I’m also not a big fan of having multiple dependencies in my applications, so I’m jumping on the possibility to remove two of them.
So here’s how it works. Keep in mind that it may not be a perfect solution or even a recommended one, but it’s the one I use in my projects and works. But let’s stop chatting and dive into the code.
I’m working with a single state file I call Provider. It contains everything to handle the state. In this first sample, it’s just a getter and a setter I receive from a state hook.
This looks much simpler and more efficient, but there are still a few issues to improve it:
- The getters and setters are in the same object, which is a bit of a mess.
toggleAvailabilitymethod is managed in the children component, which is not functional.
- We will probably encounter performance issues due to our state change.
For the first one, I like to cut the object into two sub-objects,
values , like dispatchers and states in Redux. It eventually looks like this:
For the second one, we just need to move the call into the parent component and add the action in our new actions section. It will make our
Beer component a lot simpler.
As for performance, we still have two issues in our component:
toggleAvailabilitymethod will be re-evaluated every time the
Providercomponent is updated
- The value object which contains the state will also be updated every time the
Providercomponent has a change.
Fortunately, React provides two hooks to handle a cache of our data.
We will first encapsulate the
toggleAvailability method in the
useCallback hook. It will ensure the returned method will always be the same when the data in the second parameter has not changed. This will be possible because React’s
useState hook guaranteed its set method would be the same despite the renders.
Then we’ll use the
useMemo hook to encapsulate the
value object. This hook is almost the same as
useCallback but for objects. It will also get a second parameter to show what data it depends on.
And that’s all, folks! We no longer have Redux in our application and have a clean Context usage. I hope you give the Context API a try!