A Dummy’s Guide to Redux and Thunk in React

If, like me, you’ve read the Redux docs, watched Dan’s videos, done Wes’ course and still not quite grasped how to use Redux, hopefully this will help.

It took me a few attempts at using Redux before it clicked, so I thought I’d write down the process of converting an existing application that fetches JSON to use Redux and Redux Thunk. If you don’t know what Thunk is, don’t worry too much, but we’ll use it to make asynchronous calls in the “Redux way”.

This tutorial assumes you have a basic grasp of React and ES6/2015, but it should hopefully be easy enough to follow along regardless.

The non-Redux way

Let’s start with creating a React component in to fetch and display a list of items.

Laying the foundations

First we’ll setup a static component with a that contains various to output, and 2 boolean states to render something different when it's loading or errored respectively.

It may not seem like much, but this is a good start.

When rendered, the component should output 4 list items, but if you were to set or to , a relevant would be output instead.

Making it dynamic

Hard-coding the items doesn’t make for a very useful component, so let’s fetch the from a JSON API, which will also allow us to set and as appropriate.

The response will be identical to our hard-coded list of items, but in the real world, you could pull in a list of best-selling books, latest blog posts, or whatever suits your application.

To fetch the items, we’re going to use the aptly named Fetch API. Fetch makes making requests much easier than the classic XMLHttpRequest and returns a promise of the resolved response (which is important to Thunk). Fetch isn’t available in all browsers, so you’ll need to add it as a dependency to your project with:

The conversion is actually quite simple.

  • First we’ll set our initial to an empty array
  • Now we’ll add a method to fetch the data and set the loading and error states:
  • Then we’ll call it when the component mounts:

Which leaves us with (unchanged lines omitted):

And that’s it. Your component now fetches the from a REST endpoint! You should hopefully see appear briefly before the 4 list items. If you pass in a broken URL to you should see our error message.

However, in reality, a component shouldn’t include logic to fetch data, and data shouldn’t be stored in a component’s state, so this is where Redux comes in.

Converting to Redux

To start, we need to add Redux, React Redux and Redux Thunk as dependencies of our project so we can use them. We can do that with:

Understanding Redux

There are a few core principles to Redux which we need to understand:

  1. There is 1 global state object that manages the state for your entire application. In this example, it will behave identically to our initial component’s state. It is the single source of truth.
  2. The only way to modify the state is through emitting an action, which is an object that describes what should change. Action Creators are the functions that are ed to emit a change – all they do is an action.
  3. When an action is ed, a Reducer is the function that actually changes the state appropriate to that action – or returns the existing state if the action is not applicable to that reducer.
  4. Reducers are “pure functions”. They should not have any side-effects nor mutate the state — they must return a modified copy.
  5. Individual reducers are combined into a single to create the discrete properties of the state.
  6. The Store is the thing that brings it all together: it represents the state by using the , any middleware (Thunk in our case), and allows you to actually actions.
  7. For using Redux in React, the component wraps the entire application and passes the down to all children.

This should all become clearer as we start to convert our application to use Redux.

Designing our state

From the work we’ve already done, we know that our state needs to have 3 properties: , and for this application to work as expected under all circumstances, which correlates to needing 3 unique actions.

Now, here is why Action Creators are different to Actions and do not necessarily have a 1:1 relationship: we need a fourth action creator that calls our 3 other action (creators) depending on the status of fetching the data. This fourth action creator is almost identical to our original method, but instead of directly setting the state with , we'll an action to do the same: .

Creating our actions

Let’s create an file to contain our action creators. We'll start with our 3 simple actions.

As mentioned before, action creators are functions that return an action. We each one so we can use them elsewhere in our codebase.

The first 2 action creators take a (/) as their argument and return an object with a meaningful and the assigned to the appropriate property.

The third, , will be called when the data has been successfully fetched, with the data passed to it as . Through the magic of ES6 property value shorthands, we'll return an object with a property called whose value will be the array of ;

Note: that the value you use for and the name of the other property that is returned is important, because you will re-use them in your reducers

Now that we have the 3 actions which will represent our state, we’ll convert our original component’s method to an action creator.

By default, Redux action creators don’t support asynchronous actions like fetching data, so here’s where we utilise Redux Thunk. Thunk allows you to write action creators that return a function instead of an action. The inner function can receive the store methods and as parameters, but we'll just use .

A real simple example would be to manually trigger after 5 seconds.

Now we know what a thunk is, we can write .

Creating our reducers

With our action creators defined, we now write reducers that take these actions and return a new state of our application.

Note: In Redux, all reducers get called regardless of the action, so inside each one you have to return the original if the action is not applicable.

Each reducer takes 2 parameters: the previous state () and an object. We can also use an ES6 feature called default parameters to set the default initial .

Inside each reducer, we use a statement to determine when an matches. While it may seem unnecessary in these simple reducers, your reducers could theoretically have a lot of conditions, so // will get messy fast.

If the matches, then we return the relevant property of . As mentioned earlier, the and is what was defined in your action creators.

OK, knowing this, let’s create our items reducers in .

Notice how each reducer is named after the resulting store’s state property, with the not necessarily needing to correspond. The first 2 reducers hopefully make complete sense, but the last, , is slightly different.

This is because it could have multiple conditions which would always return an array of : it could return all in the case of a fetch success, it could return a subset of after a delete action is ed, or it could return an empty array if everything is deleted.

To re-iterate, every reducer will return a discrete property of the state, regardless of how many conditions are inside that reducer. That initially took me a while to get my head around.

With the individual reducers created, we need to combine them into a to create a single object.

Create a new file at .

We import each of the reducers from and export them with Redux's . As our reducer names are identical to what we want to use for a store's property names, we can use the ES6 shorthand.

Notice how I intentionally prefixed my reducer names, so that when the application grows in complexity, I’m not constrained by having a “global” or property. You may have many different features that could error or be in a loading state, so prefixing the imports and then exporting those will give your application's state greater granularity and flexibility. For example:

Alternatively, you could alias the methods on , but I prefer consistency across the board.

Configure the store and provide it to your app

This is pretty straightforward. Let’s create with:

Now change our app’s to include , , set up our and wrap our app () to pass the down as :

I know, it’s taken quite a bit of effort to get to this stage, but with the set up complete, we can now modify our component to make use of what we’ve done.

Converting our component to use the Redux store and methods

Let’s jump back in to .

At the top of the file, what we need:

is what allows us to connect a component to Redux's store, and is the action creator we wrote earlier. We only need to import this one action creator, as it handles ing the other actions.

After our component’s definition, we're going to map Redux's state and the dispatching of our action creator to props.

We create a function that accepts and then returns an object of props. In a simple component like this, I remove the prefixing for the / props as it’s obvious that they're related to .

And then we need another function to be able to our action creator with a prop.

Again, I’ve removed the prefix from the returned object property. Here is a function that accepts a parameter and returns ing .

Now, these 2 and don't do anything yet, so we need to change our final line to:

This s our to Redux while mapping the props for us to use.

The final step is to convert our component to use instead of , and to remove the leftovers.

  • Delete the and methods as they're unnecessary now.
  • Change in to .
  • Change to for , and .

Your component should now look like this:

And that’s it! The application now uses Redux and Redux Thunk to fetch and display the data!

That wasn’t too difficult, was it?

And you’re now a Redux master :D

What next?

I’ve put all of this code up on GitHub, with commits for each step. I want you to clone it, run it and understand it, then add the ability for the user to delete individual list items based on the item’s .

I haven’t yet really mentioned that in Redux, the state is immutable, which means you can’t modify it, so have to return a new state in your reducers instead. The 3 reducers we wrote above were simple and “just worked”, but deleting items from an array requires an approach that you may not be familiar with.

You can no longer use to remove items from an array, as that will mutate the original array. Dan explains how to remove an element from an array in this video, but if you get stuck, you can check out (pun intended) the branch for the solution.

I really hope that this has clarified the concept of Redux and Thunk and how you might go about converting an existing React application to use them. I know that writing this has solidified my understanding of it, so I’m very happy to have done it.

I’d still recommend reading the Redux docs, watching Dan’s videos, and re-doing Wes’ course as you should hopefully now be able to understand some of the other more complex and deeper principles.

This article has been cross-posted on Codepen for better code formatting.

Lead UX Engineer at @nrl. An author, speaker, artist & open sourcerer http://github.com/stowball . Devoted to #a11y, CSS, #vuejs, alt-rock, Nintendo & Charlie P

Lead UX Engineer at @nrl. An author, speaker, artist & open sourcerer http://github.com/stowball . Devoted to #a11y, CSS, #vuejs, alt-rock, Nintendo & Charlie P