Redux with React Router
React Router is a routing library for React, which was inspired by Ember’s router. It is easy to use and fully featured.
One feature of Ember’s router that I found very useful is that you can provide each route a model method, which is responsible for fetching it’s initial data. When the method returns a Promise, Ember delays the rendering of the route’s template and optionally renders a loading template instead until the Promise becomes resolved. Although this behaviour is missing from React Router, it is very easy to implement with the use of Redux.
The Example Application
In this article, I am going to show you how to use Redux and React Router together to build a simple application. The application is going to have a list page and a details page featuring cats.
Whenever the user navigates to the cat list page, the application has to fetch the list of cats from an API. While the request is pending, the application has to show a loading message. If it fails, the application has to show an error message. If it ends successfully, the application has to show the list of cats, with links to the cat details pages.
Whenever the user navigates to the cat details page, the application has to fetch the details of a single cat (identified by the URL). While the request is pending, the application has to show a loading message. If it fails, the application has to show an error message. If it ends successfully, the application has to show the details of the cat.
First, initialise the application using npm:
Install the dependencies:
Add the following start and build scripts to the package.json file:
At this stage, the package.json file should look similar to this:
Create a .bablerc file, in the root folder of the project with following content:
Create some empty folders for later use:
Finally, create the following index.html file in the public folder:
To feed the application, we need an API, with two endpoints. One to provide the list of cats, and another to provide the details of a single cat. We are going to use Express to implement those endpoints.
Note: Data borrowed from WikiPedia.
The first endpoint (line 38) provides the list of cats and the second (line 44) provides the details of a single cat. Both have a 3 seconds delay. That is deliberate, as it makes it easier to test the application’s behaviour when a request to the API is in progress.
Now we can start up the server and test the API using curl.
Start up the server:
Get the list of cats:
Get the details of a single cat:
Everything works as expected!
The API is ready, we can start to work on the application. First, we need to define the action type constants, and the action creators. To do that, we have to identify the list of events that suppose to change our application’s state. Based on the requirements above there are six, three per page:
- We send a request to the list endpoint.
- We receive an error response from the list endpoint.
- We receive a success response from the list endpoint.
- We send a request to the details endpoint.
- We receive an error response from the details endpoint.
- We receive a success response from the details endpoint.
Action type constants:
Next, we have to define the action creators for each action type and two more to send the actual requests to the API. For the asynchronous action creators, we are going to use the Redux Thunk middleware and the isomorphic-fetch library.
Cat list action creators:
Cat item action creators:
The reducers role is to mutate the application’s state based on the actions. We know the latter, but not yet the prior. My typical method of designing an application’s state is that I list the questions that it has to answer. For this tutorial, the following questions could be listed:
- Is there a list request in progress?
- Did the last list request fail?
- What was the response of the last list request?
- Is there a details request in progress?
- Did the last details request fail?
- What was the response of the last details request?
One possible state what we can come up with:
The state above gives us the following answers:
- There is no list request in progress, because cats.isFetching is false.
- The previous list request did not fail, because cats.error is null.
- The response of the previous list request is the same as cats.cats.
- There is no details request in progress, because cat.isFetching is false.
- The previous details request did not fail, because cat.error is null.
- The response of the previous details request is the same as cat.cat.
Now we know what kind of application state we need, and what kind of actions we can expect. Therefore, it is time to build the reducers. To make sure that the state is immutable we are going to use facebook’s Immutable library.
Cat list reducer:
Cat item reducer:
We now have everything we need to prepare the application’s store. This is the point where we plug in the Redux Thunk middleware as well.
For this tutorial we only need two components. One for the list page and another for the details page. In a real life application these components could be broken up into many smaller ones, but I would like to keep it simple.
Cat list page component:
Cat page component:
It is time to connect the components to Redux with containers and to define when should the list and details requests be fired.
Cats page container:
As you can see, we send a list request whenever a user navigates to the list page (line 20).
Cat page container:
This is a little bit more difficult. We send a details request whenever a user navigates to the details page (line 20) and whenever a user who is already on the details page changes the ID parameter in the URL (line 24). You can read the detailed explanation of the lifecycle of routed components’ here.
Application Entry Point
Finally, we put everything together in application’s entry point.
Building and Testing
Build the application:
If the server is still running, you can open the app in your browser using the following URL: http://127.0.0.1:3000.
Let’s try it!
Everything works as expected!
You have learned:
- What Redux is good for.
- What React Router is good for.
- What is missing.
- A pattern to build data fetching logic for routed components.
The complete source code with additional tests available here.
Thank you for reading!