Using the useReducer Hook
This final lesson eschews all external redux libraries and swaps in the use of React's built-in useReducer Hook and Context tools.
Using the useReducer Hook#
In this third and final lesson of the module, we’re going to go over another redux alternative, this one using no external libraries. Instead, we’ll be using a combination of React’s own
useReducer Hook and the built-in Context system to pass both
state and a dispatching function to various components.
You may remember we briefly introduced the
useReducer Hook in our React Hooks Deep Dive module, but said we’d be covering it later on. Well that time has come, so let’s get learning!
By the way, I also have a great article on this subject available on my own website if you’d like to see more examples of the
useReducerHook in action.
Using useReducer and Context#
You call the Hook, passing in a reducer function, initial state value (shown here as
initialArg) and an optional third argument,
init, which would be a function that allows you to lazy load your initial state value.
What the Hook returns is an array containing the current
state of the app and a
dispatch function to trigger
state updates via actions, just as we have been doing so far.
useReducer Hook focuses on what it does so well, but we still have a few missing pieces of the puzzle. For example, we no longer inherently have any central store mechanism, nor an obvious means to pass access to our app’s
state or the
dispatch function to different components. It’s not the end of the world, but it's common practice to create a centralized "store" mechanism just like we’ve seen with the other redux approaches this far, and then pass both the
dispatch items across our app via React’s Context system.
Updating the project#
We have a few changes to make to our project again, starting with removing the React Redux libraries, and working our way through the same files from the last lesson to switch over to using the
Before we dive in, I’m just going to give you a heads-up that when we’re done, the resulting code will look a lot more like the first lesson’s code than the much more concise approach offered in the last lesson using Redux Toolkit.
Personally I feel that the
useReducer and Context approach is somewhere between the two lessons we’ve covered so far. It is not as concise or smooth as that offered by the Redux Toolkit, but it does offer a much less opinionated approach, and doesn’t depend on any external libraries.
What’s more, the way we're going to tackle it here is one of a few different ways you could employ the
At its simplest it is a single line function call that is paired with a
switch statement that updates some object values in a
We’re going to be taking it up a gear in this lesson by creating:
a central redux store
a Context provider wrapper
and a combined reducer function just like we’ve already seen, to keep things equal between the three lessons.
If you decide that your project needs a redux system it's up to you to decide which type of implementation you prefer. There's no right or wrong solution or indeed a one-solution-fits-all approach. Sometimes, using the built-in offerings of
useReducerand Context that we're exploring here will be enough. If you prefer a more opinionated and structured approach then the Redux Toolkit will work best for you. Once we've finished the module you'll have a more informed set of choices that you can explore using your own ideas and projects. I'd highly recommend playing around with them in a code playground, such as CodeSandbox.io, to find which works best for you.
Switching up dependencies#
Just like the last lesson, let’s start by removing what we don’t need from our project. In this case, we’re going to remove the two packages,
This time I’m just going to issue the yarn command,
yarn remove @reduxjs/toolkit react-redux.
With that done, let’s start with some of the foundational work and build the changes up from there.
Dead easy one to start with - find the
/config folder that contains the
configureStore.js file and just delete it altogether. We’ll still be creating the idea of a store, but we’ll do the work in our central
reducers.js file this time.
This folder and file would be unused dead weight, so get it deleted and let’s move on.
We’re beginning our edits with the
index.js file this time as there’s very little to change and it’s largely all removals.
Open up the file and let’s change up the imports around redux things:
Notice that the top imports are the same, but we’ve removed the
store we brought in previously from our
configureStore.js file which no longer exists.
We’ve also changed the
Provider import to
StoreProvider and changed the import location from
react-redux to our
reducers.js file. We’ll build out this new
StoreProvider component later, but let’s import it here and use it right now.
StoreProvider import at the ready, simply use it to directly replace the
<Provider> component that’s currently wrapping our
The changed file should look like this:
Let’s move onto the
eventReducer.js file. Open it up and familiarize yourself with the current code we used alongside the Redux Toolkit. Unfortunately, our new code won’t look quite as neat and tidy as this, but it won’t be as long and unwieldy as in the first lesson.
Remove everything in this file except for the
uuid import at the top.
There we go, pretty much identical to those from the first lesson. Next, let’s create our
eventReducer variable, complete with
switch statement to work through the possible action types, and export it from the file as the default export.
This should look very familiar as it's virtually identical to the reducer function from lesson 1. There are a couple of subtle differences here, mainly where we’re referencing the
action.payload value directly, rather than extracting it via destructuring into separate variables.
That aside, we still have to create a copy of
state with our updates applied and return it from our matching cases. Everything else should look about the same though and it’s a pattern that you’ll start to see again and again when you get more adept with redux - this idea of adding items to arrays, changing values in a particular item in an array, and removing items from an existing array. You’ll see heavy and frequent use of both the
filter() functions as they both return a copy of an existing array which is perfect for us as we don’t want to affect the original
With all that wrapped up, let’s save and close the completed file which now looks like this:
We’re going to move on to arguably the most complex edits now, mainly due to the fact that we’re having to roll our own store and provider offerings. We’ll be introducing some unexplored concepts here. We didn’t have to do quite as much wiring up in the previous lessons because React Redux helps out a lot and Redux Toolkit offers a big dollop of black magic, doing almost everything for us.
Open up the
reducers.js file and clear it out completely. Both the previous lessons imported the other reducers into this file and merged them all into a single offering using the
combineReducers function. We don’t have access to such a function now that we’re on our own, so we’ll have to make one.
We’re also going to need to create a helper function that will create actions for us, as well as building a store context and adding both
dispatch items to it so that other components can consume them across the app.
Let’s start with some imports. We’re pulling in a lot of things from React, including the new
useReducer Hook. We’ve got our initial state and our events reducer that we just finished changing.
Next, we’ll create our own
It might look a little fussy and tricky to read, but we’re effectively going to pass this function an object that has a set of key:value pairs representing
reducerKeyName:actualReducerFunction. What we do then is return a sub-function that accepts a
action argument. It is this sub-function that will be called by the
useReducer Hook (as we’ll see in a moment). It almost acts like a reducer factory of sorts.