I remember the old days of Redux fondly.
Long ago, when hooks were not yet on anyones’ mind.
When Redux required us to wrap every React component in higher-order
connect() components, with functions mapping dispatch and state to props.
A barbaric time where our actions lived one place, and our reducers another.
Where both thunks and Redux devtools required additional dependencies, and we had to write a whole bunch of code to do what boils down to very little: updating application state.
But then, whispers began of a creature rising in the distance: React Hooks.
And with it followed a tidal wave of change…
…class components, banished!
Lifecycle methods, deprecated, littering the ground like skeletons.
But from this change, life bloomed…
Alright, enough of this ridiculous metaphor. 😅
We both know you are not here for cheesy writing.
With Redux Toolkit we can approach Redux in a new way, with far less code, while leverage hooks.
In this tutorial I will show you how.
Together we are going to begin with
create-react-app, set up Redux using RTK, and write a thunk to fire off an API fetch with async/await to retrieve some data from a recipes database as our main component mounts.
We will store this data in Redux using slices, write logic for error handling, and retrieve and display the data in the browser.
All of the above will be accomplished using both React and Redux Hooks, and less code than we would using the older Redux methods.
Here’s the final repo if you’d like to skip ahead.
Let’s begin by leveraging
create-react-app and the adding the few dependencies we'll need.
In your console, navigate to where you keep your projects (ie. Documents, Desktop, Tax-Receipt-2015) to set up the React boilerplate.
I’ll be calling this project
> npx create-react-app redux-toolkit-2020
With the install completed, let’s change into our project directory and add the dependencies we’ll need for this project, then boot up the app.
> cd redux-toolkit-2020 > yarn add redux react-redux @reduxjs/toolkit > yarn start
Alright, works as it’s supposed to. Shut down the app with
control + c and let's get to work.
We’re going to start by removing all the boilerplate files in
That’s right, delete it all.
cd into the now empty
/src folder and create the files
> cd src > touch index.js index.css App.js
Your project tree should now look like so:
Now let’s set up a simple React component for us to start with.
index.js, add the following block of code:
App.js create a simple React component.
Lastly, let’s just establish some basic styles so our UI looks passably okay.
Add the following to
Now start the app again to ensure all is working as expected…
… and we are looking good.
The last thing we need to do is to install the Redux Devtools extension for Chrome.
This browser extension will allow us to examine the contents of our Redux store from the developer panel, and is a must-have for anyone working with Redux.
With this super-basic React app set up for us to build upon, let’s get started with Redux Toolkit!
Redux Toolkit abandons the use of higher-order components with the
connect() method and embraces the use of Hooks and Slices.
React Hooks allow us to handle updating local state and using lifecycle methods in functional components with
Redux Hooks allow us to retrieve data from the store and dispatch actions to update the store with
Let’s begin by creating a new directory in
> mkdir src/slices > touch src/slices/index.js src/slices/recipes.js
index.js file in
/slices will be our
Here is were you will import all of your various reducers for the Redux state and combine them with
slices/index.js, create our
rootReducer is only importing a single reducer,
recipesReducer, called so because we will be querying a database of recipes and storing that data in our store as an array of recipe objects.
Add the following code to
slices/recipes.js and we will go over what we are doing here together.
This may look a little overwhelming at first, so let’s go through what is happening, starting at the top.
At the top of the file we import
createSlice from Redux Toolkit, and we follow this by creating the initial state.
The initial state is just that: it is the base object of our
recipes state that we create to store our
recipes data. In this initial state object we have 3 values:
Since we will be fetching data from an external database, we need a way to indicate the success or failure of that request to the UI so that we may update it accordingly, and we accomplish this by firing actions to update the states of
Beneath the initial state is our
slice and where the magic of Redux Toolkit happens.
We create the slice with the method
createSlice() that we imported, and we store this in a variable to be exported as our
Within the slice object we establish the name (recipes), pass in the initial state object that we created above, and define the reducers.
The reducers take in
state and optionally a
payload and update the state accordingly.
For example, in the block for the
getRecipesSuccess() function, when called it will receive a payload object (an array of recipes objects) and will add the payload data to our recipes array in the state, while also signalling that the query has been successful by setting
With our recipes slice established and our root reducer created, all we need to do is to create the redux store and wrap our app in it in
index.js at the folder root (where we first import
Now just open the devtools in Chrome and you will see our store nicely set up and awaiting some data 😄.
In order to pull data from our Redux store into a functional component, we need to write a selector function alongside our recipes slice.
/slices/recipes.js, add the following selector. You can add it directly after the const
The selector merely exports the current state of the recipes array.
Then, back in
App.js, we will use this selector by importing the
useSelector() hook from
react-redux, as well as the
recipesSelector we just created in our recipes slices.
In your Chrome developer console you will see the result of our selector: an empty recipes array.
Now let’s get that data, yes?
Let’s go back to our
slices/recipes.js file and write a thunk.
A thunk is a middleware that lets us call a function to do something, and which results in dispatching Redux actions to update the store.
We are going to write an asynchronous thunk function that will be dispatched when our app loads for the first time.
This thunk function will begin by updating our Redux
loading state to
It will then await a fetch call to TheMealDB API within a
On success, we will dispatch an action to update the store with our recipes.
On failure, we will update the store with the error returned.
/slices/recipes.js, export our 3 slice reducers as actions, then create a separate function called
recipes.js will now look something like this:
Take a little time to follow the information flow starting from
Now, let’s use the React hook
useEffect() and the Redux hook
useDispatch() to fire off this thunk on initial mount of
App.js with the following:
Now if we take a look at our Redux devtools, we should see that our fetch was successful, and that our recipes array now has a whole bunch of data in it! 😁
The last thing to do is to display this data for the user in our UI.
Since we already implemented our selector to grab the states for
hasErrors, we can do this fairly effortlessly.
First though, let’s update one little thing in our thunk.
You may have noticed that the API returned a meals array containing the recipe objects. We don’t really need the meals key, so update the line in
slices/recipes.js in the thunk function
fetchRecipes() to dispatch
getRecipesSuccess and pass
data.meals, like so:
App.js, update it one last time to return
loading if those are true, and to map over the recipes array if the fetch is successful.
Our final product!
Redux has grown considerably since I began using it about 3 years ago, and personally, I love these improvements.
Being able to easily update the store and handle app state logic with hooks is a godsend.
Be sure to read the Redux Toolkit API to learn more about what is possible.
Here’s the final repo for the finished app.