React, Redux and Routing

In the process of dealing with my application, I’ve got a curious combination. My React application is using react-router to handle page transitions within the single-page application. It does this by putting the page that is currently being shown in the hash of the location. As a result, the pages end up looking like http://localhost:3000/#/home?q=982hihu.

However, I’m using Redux as my application state store. The idea is that all my application state is in the state store. Except for the react-router state – that is stored in the location hash.

This is obviously less than ideal. I want my application state to be in the redux store – not in other places. More importantly, this is something that others have had to deal with as well as there are packages out there that handle this situation. I’m going to integrate the react-router with Redux using react-router-redux.

Step 1: Update the Store

First off, install the react-router-redux package:

npm install --save-dev react-router-redux

Now update the client/redux/store.js with the code necessary to integrate the library:

import { createStore, combineReducers, applyMiddleware } from 'redux';
import createLogger from 'redux-logger';
import thunkMiddleware from 'redux-thunk';
import promiseMiddleware from 'redux-promise';
import { createHistory } from 'history';
import { syncHistory, routerReducer } from 'react-router-redux';

import { requestAuthInfo } from './redux/actions/auth';
import * as reducers from './redux/reducers';

// Combine the reducers for the store
const appReducers = combineReducers({
    routing: routerReducer

// Move the react-router stuff into Redux
export const history = createHistory();
const reduxRouterMiddleware = syncHistory(history);

// Apply all the middleware for Redux
const middlewares = applyMiddleware(

// Create the Redux Store
export const store = reduxStore(appReducers, middlewares);
// reduxRouterMiddleware.listenForReplays(store);

// Dispatch the initial action

There are a couple of wrinkles here. Firstly, you may not be familiar with using spread operators to combine objects. Let’s say I have the following:

const a = { b: 1, c: 2 };
const d = {
    d: 3
// d === { b: 1, c: 2, d: 3 };

This is an excellent way of merging objects with ES2015 shorthand. The result is that I have three reducers – the two I provide and the routeReducer that react-router-redux provides.

The second wrinkle is that I actually need to link history to both the Router object and to the store. it’s the go-between for the two pieces.

After those two wrinkles, the store set up is fairly simple. I’m intending on setting up replay at some point so I’ve put a reminder to listen for replays for the store. It’s not important or needed right now.

Step 2: Update the Router

My main app.jsx file contains the router. I’ve updated it to listen to the exported history object from the store:

import React from 'react';
import ReactDOM from 'react-dom';
import injectTapEventPlugin from 'react-tap-event-plugin';
import { Provider } from 'react-redux';
import { Router } from 'react-router';
import { StyleRoot } from 'radium';
import AppRoutes from './pages';
import { store, history } from './redux/store';

// Needed for onTouchTap - Can go away when react 1.0 release
// Check this repo:

let pageStyle = {
    bottom: 0,
    left: 0,
    margin: 0,
    padding: 0,
    position: 'fixed',
    right: 0,
    top: 0

let onUpdate = () => { window.scrollTo(0, 0); };

// render the page
    <Provider store={store}>
        <StyleRoot style={pageStyle}>
            <Router history={history} onUpdate={onUpdate}>

This code is fairly simple. Line 8 brings in both the store and the history that we export from the redux/store.js module. Line 30 links the router to our redux-enabled history object.

Step 3: Update the routing actions

Normally, one would use LinkTo from the react-router object to update the route. With redux in the mix, one will dispatch an action to update the routing context. Here is the Home.jsx component view that shows the general form of the new linkage:

import Radium from 'radium';
import React from 'react';
import { connect } from 'react-redux';
import { routeActions } from 'react-router-redux';

class Home extends React.Component {
     * React property types
     * @type {Object}
     * @readonly
    static propTypes = {
        dispatch: React.PropTypes.func.isRequired,
        phase: React.PropTypes.string.isRequired

     * Render the component
     * @returns {JSX.Element} the rendered component
     * @overrides React.Component#render
    render() {
        const dispatch = this.props.dispatch;
        const page1 = () => { return dispatch(routeActions.push('/page1')); };

        return (
            <div id="homePage">
                    <li><button onClick={page1}>Page 1</button></li>

** Link the Chrome component to the Redux store
export default connect(
    (state) => {
        return {
            phase: state.auth.phase

Line 4 brings in the routeActions – a set of action creators provided by the react-router-redux library. The main action creator here is push which pushes the requested route onto the history and hence changes the page that you are on.

A Small Issue – the page route

One of the small issues I had was in the reload case. Let’s say I go to http://localhost:3000 – this loads my application, exactly as expected. then I click on the Home page – this changes the URL to http://localhost:3000/home and the home page is displayed. If I then click the reload button, I get an error message.

The problem is that the route (without the hash) is sent to the server and the server does not know how to handle it. Basically, I need to ensure that any unknown routes cause the server to load the initial page. This is done in my server/static/index.js module – this module serves my home page:

var config = require('config');
var express = require('express');
var fs = require('fs');
var path = require('path');
var serveStatic = require(`./static.${config.get('env')}.js`);

var fileContents = {};
var router = express.Router(); // eslint-disable-line new-cap

 * Load the specified HTML file, caching the contents
 * in the fileContents object.
 * @param {string} filename the file name to load
 * @returns {string} the contents of the file
function loadHtmlFile(filename) {
    var contents = '', file = path.join(__dirname, filename);
    if (!Object.hasOwnProperty(fileContents, filename)) {
        contents = fs.readFileSync(file, 'utf8'); // eslint-disable-line no-sync
        fileContents[filename] = contents
            .replace(/\$\{config.base\}/g, config.get('base'))
            .replace(/\$\{config.env\}/g, config.get('env'))
            .replace(/\$\{config.library.font-awesome}/g, config.get('library.font-awesome'))
            .replace(/\$\{config.library.mdi}/g, config.get('library.mdi'))
            .replace(/\$\{config.library.core-js}/g, config.get('library.core-js'))
    return fileContents[filename];

// Home Page
router.get('/', (request, response) => {
    return response

// Service static files - different for dev vs. prod

// Ensure that anything not routed is captured here
router.get(/.*/, (request, response) => {
    return response

module.exports = router;

The highlighted lines get called AFTER everything else. This means that anything that isn’t already responded to will return the home page. Of course, this means that if you go to http://localhost:3000/foo (or any other page that doesn’t have a route), then you will get a blank page unless you have a default route. Have a default route. You can do this easily in your route definition:

const routes = (
    <Route path="/" component={Chrome}>
        <IndexRoute component={Home}/>
        <Route path="home" component={Home}/>
        <Route path="page1" component={PageOne}/>
        <Redirect path="*" to="/" />

The Redirect needs to be the very last route. It basically says “anything that hasn’t already been matched, redirect to the home page”. Pretty cool.

Wrap Up

Another day – another React library. I like the modularity, but I’m thinking that there must be a Yeoman generator or something to enable a lot of this scaffolding code. I’m just cringing at adding yet another module or library to my code.

However, that’s reality in the React world. As always, my code is on my GitHub Repository.

2 thoughts

Comments are closed.