This tutorial will provide a gentle introduction to Redux in the context of a React app. At the end I will cover a couple advanced topics. My goal is to show you some common patterns when building React apps with Redux and provide my take on them.
Redux is a uni-directional data flow implementation inspired by Flux. If you’ve read any of the numerous Flux articles on the web, this might already be familiar to you. If you’ve never heard of this before, I encourage you to read more about it.
Let’s add the necessary npm dependencies to our app.
Now we’ll create a store, define our routes and render the app.
What we’ve done here is first create a Redux store with a single reducer. We’ve defined our routes and rendered the whole thing into an element who’s id is
If you’re going to be writing React apps with Redux,
react-redux is worth a look. It provides interfaces for React and Redux to work together. The
<Provider> component receives a reference to the Redux store allowing all our components to listen to that store.
The Main Component
Redux aside, we’ll start with the most basic React component that I’ll call
Main. This is a pure stateless component. Nothing about this component tells it to listen to the Redux store because we haven’t defined it yet.
Redux Action Creators
An action creator is responsible for producing an action of a specific type. In practice, the action that is produced should be as simple as possible. It shouldn’t modify the state of the store, it should merely provide a type and optionally some payload information that will be used to modify the store state by a reducer.
Here is a simple search action creator. The
search function will create an action with type
SEARCH and provide a
query payload. Your action should define a
type that will be read by a reducer later to determine how the state of the store needs to be updated.
This reducer will look for the
SEARCH action type and filter an array of objects using the
query payload past in on the action. Both the
results will be used to update the state of the store. Pay attention to the
return statement of the
SEARCH case as I’m building a new object by applying the
... spread operator to the existing
state and overriding the
results properties with new values.
I’ve also provided the shape of our store with the
initialState constant. In the reducer’s method signature, if
state is undefined, it will receive the value of
initialState, allowing components to render without any undefined errors. This is typically when the page first loads.
Making Components Listen to Redux
Now that we have an action, a reducer and an initial store state, our
Main component can be updated to listen to the store.
There’s a fair bit more going on here than in our humble
Main component that we defined earlier. This component is still stateless. All the data and callbacks are received as props.
Have a look at the last line. Instead of exporting the component, I’m exporting the result of a function call. The
connect function from
react-redux gets passed some mapper functions. The result is another function that gets called with the
Main component as its argument.
The first mapper function
mapStateToProps receives the entire state tree of the Redux store. Using object destructuring, I can tell it to only listen for specific properties.
The second mapper function
mapDispatchToProps receives the Redux
dispatch function as it’s only argument. This function needs to apply the
dispatch function to any action creators the component will need. Without
dispatch will be passed to the component directly and you’ll have to wrap all your calls manually. By using
bindActionCreators from Redux, it will wrap my action creators with
dispatch and provide the bounded functions as props on the component.
Main component itself receives three props. The wrapped
search action creator,
results. I’m calling
search from the
onChange event of an input field in order to dispatch the action using the user’s search criteria.
This is the basic pattern that I tend to follow when using Redux in a React app. It’s a bit of of boilerplate, but it promotes cleaner components in my opinion.
Async Redux Action Creators
Moving on to a slightly more advanced topic, let’s look at async action creators. At the moment our
Main component isn’t very useful. Let’s update things to fetch results from a database using an ajax call. We’ll first add the
redux-thunk dependency, which allows our action creators to return functions as well as objects. Then we’ll update our store definition.
I’ve left some code out here, you can refer to the setup section at the top of this post.
Now we can create an async action creator. I’ve replaced the
SEARCH type with
RECEIVE_RESULTS, added the
receiveResults action creator, and updated the
search action creator to make a server request.
search no longer returns an action object, it now returns a function. That function receives one parameter, which will be the Redux
dispatch function allowing us to dispatch actions later. In this example,
getResource makes the server request and returns a promise. When that promise resolves, I will dispatch a call to
receiveResults and pass it the
query entered by the user and the
results from the server. If the promise is rejected, it will be caught and throw an error message. You can choose to treat rejected promises how you like.
search make an async server request, I can update the search reducer. The store state no longer needs the
characters property, so we don’t need to filter our results either. We simply return a new state, overriding
results from the server. There’s really nothing else we need to do. The
Main component doesn’t need to be changed, it doesn’t care that we’re getting the results from the server, it’s completely ignorant of that fact.
Working with Multiple Reducers
Another advanced pattern I use is splitting my action creators and reducers into domain specific modules. Where the
Main component was responsible for searching and listing results, we might want another component to view the details related to those results, along with it’s own actions. Let’s start with another async action creator.
The following reducer treats the
RECEIVE_CHARACTER_PROFILE action type I just defined and updates the Redux store with the character profile returned from the server.
With the addition of the character profile reducer, we can use the
combineReducers function from Redux and redefine the root reducer. By passing
combineReducers, I’m defining the Redux store with some top level namespaces that will each contain a part of the store’s state. I’ve also added a new route here to view a single character profile.
Main component, it now renders a list of links that point to character profile pages, using React Router’s
Link component. The
mapStateToProps function has been updated too. Now it subscribes to the
search namespace of the Redux store and gets the
results properties from there.
Finally let’s create the
CharacterProfile component. It’s another stateless component like
Main, but made even simpler because it doesn’t define a
mapDispatchToProps function. It isn’t required here because no actions will be dispatched from this component. It merely defines a
mapStateToProps function which will listen to the
characterProfile namespace in the Redux store.
My Final Thoughts
Every once in a while in my career I’ve come across some API or framework that just clicks once I get it. It just makes sense. It’s one of those things that you can almost use with your eyes shut. That’s what React with Redux does for me. I wrote this entire blog post without actually testing any of the code. Although I’m confident it works, if you do find a bug, I welcome you to post a comment. Aside from the lengthiness of this post, I believe I’ve covered what somebody new to Redux is looking to achieve when they try to add it to a React app.