An Introduction to React Redux (Part 1)

I’m on vacation right now, enjoying skiing by day at the lovely town and resort of Breckenridge. I did give myself something to do over the vacation, though. Ever since I was convinced of the flux architecture, I’ve been using my own simple flux library. It’s basically using the Facebook dispatcher, but my own store implementation that is tied into component views using the componentWillMount and componentWillUnmount methods of the React API. It works and it is simple.

I’m not a fan of building a new wheel just because one can, though. If I am to maintain my own library, then it must offer something that no other library can. In the case of my flux library, that isn’t the case. It’s at the bottom of a pack of flux implementations. In addition, every single list of things that React programmers can learn is to use Redux. Redux is the flux implementation at the top of the list if you look at popularity. So I set myself a task of learning React Redux over my vacation.

My requirements are very simple – I have a single API that receives authentication information from the server. I need to initiate the request to the server and then handle the response. I have two actions in my application right now – one to do the request and one to handle the response. My code is just 214 lines of code, but it has lots of side effects. Let’s see how it goes.

With apologies to Dan Abramov – I’m sure he (and several others) will cringe as I go through this…

Step 1: Action Creators

An action is an object with a type field and potentially some associated data. Something like this:

{ type: 'AUTH-ANONYMOUS' }
{ type: 'AUTH-AUTHENTICATED', providerInfo: response }
{ type: 'AUTH-ERROR', error: error.message }

If you follow most standard flux implementations, they will tell you to create a function that dispatches the action. Redux doesn’t do that. Instead you create an Action Creator that returns an action. It isn’t dispatched (yet). This philosophy changes when we discuss async functions. However, let’s create some action creators. I’ve created a directory called redux where I am going to store all the implementation details. In there, I have an action.js that will hold my action creators:

/**
 * Redux Action Creator for handling anonymous response
 * @returns {Object} Redux Action
 */
function receiveAnonymousAuth() {
    return {
        type: 'AUTH-ANONYMOUS'
    };
}

/**
 * Redux Action Creator for handling authenticated response
 * @param {Array} response the response from the server
 * @returns {Object} Redux Action
 */
function receiveAuthenticatedAuth(response) {
    return {
        type: 'AUTH-AUTHENTICATED',
        providerInfo: response[0]
    };
}

/**
 * Redux Action Creator for handling error conditions
 * @param {Error} the error that happened
 * @returns {Object} Redux Action
 */
function receiveErrorCondition(error) {
    return {
        type: 'AUTH-ERROR',
        error: error
    };
}

These are internal actions – I am not expecting my application UI to initiate these actions. I do not export these functions because they are internal. We’ll get onto the linkage eventually. Right now there is one action creator for each of my potential results – authenticated, anonymous and error. I’ve merged all three cases in my existing code which isn’t the best, so take the opportunity to refactor the code into something more maintainable as well.

Redux suggests that you have a big list of constants for the type. However, they are just strings – in smaller applications (like mine), you can do away with the constants and just specify the strings.

Step 1A: Async Action Creators

I have another action creator – the one that initiates the request. That one was a problem. To be honest, I don’t think Redux actually deals with async well. It should “just work” and it doesn’t. Here is my function:

import fetch from 'isomorphic-fetch';

// baseUrl is required for the fetch actions
let baseUrl = '';
if (window.GRUMPYWIZARDS && window.GRUMPYWIZARDS.base)
    baseUrl = window.GRUMPYWIZARDS.base.replace(/\/$/, '');

/**
 * Redux Action Creator for requesting authentication information
 * @returns {Function}
 */
export function requestAuthInfo() {
    return (dispatch) => {
        let fetchOptions = {
            method: 'GET',
            credentials: 'include',
            cache: 'no-cache',
            mode: 'cors'
        };
        return fetch(`${baseUrl}/.auth/me`, fetchOptions)
        .then((response) => {
            if (!response.ok && response.status !== 401)
                throw new Error(`Invalid Response from ${baseUrl}/.auth/me:`, response);
            if (response.status === 401)
                return new Promise((resolve) => { resolve(false); });
            return response.json();
        })
        .then((response) => {
            if (!response)
                dispatch(receiveAnonymousAuth());
            else
                dispatch(receiveAuthenticatedAuth(response));
        })
        .catch((error) => {
            dispatch(receiveErrorCondition(error));
        });
    };
}

There is a little code at the top to figure out where the API actually is. The majority of the code is a direct copy from my original store implementation. Instead of doing the store update right there, I dispatch another action (created by one of the action creators I wrote earlier) to handle the actual action.

Step 2: Reducers

The next element in the Redux implementation is a reducer. A reducer takes a state and an action and turns it into the new state. This is a relatively simple concept. The “reducer” terminology is from the Array.reduce() functionality within JavaScript, which is designed to provide an accumulator functionality – you start with an initial value and mutate it based on each value within the array. In the case of Redux, you start with the current state of the store and you mutate it based on the action. Except that you don’t mutate the state – you return a new copy of the state. So, I guess the analogy breaks down there.

One of the golden rules of Redux is this: Reducers must be free of side effects.

In other words, if you call the reducer with the same state and the same action, it will return the same state each time. You can’t put “call this other API” or “look in a database” or that sort of thing. Right now, I’ve got one reducer (called reducers.js) that has a case statement in it to handle each action.

/* eslint-disable no-case-declarations */

const initialState = {
    phase: 'pending',
    user: null,
    error: null
};

export default function authReducer(state, action) {
    if (typeof state === 'undefined') {
        state = initialState;
    }

    switch (action.type) {
        case 'AUTH-ANONYMOUS':
            return Object.assign({}, state, {
                phase: 'anonymous',
                user: null,
                error: null
            });

        case 'AUTH-AUTHENTICATED':
            let claims = action.providerInfo.reduce((target, claim) => {
                target[claim.typ] = claim.val;
                if (claim.typ.indexOf('http://schemas.xmlsoap.org/ws') !== -1)
                    target[claim.typ.slice(claim.typ.lastIndexOf('/') + 1)] = claim.val;
                return claims;
            });

            let user = {
                accessToken: action.providerInfo.access_token,
                claims: claims,
                firstname: claims.firstname || '',
                id: action.providerInfo.user_id,
                provider: action.providerInfo.provider_name,
                providerToken: action.providerInfo.authentication_token,
                surname: claims.surname || ''
            };

            return Object.assign({}, state, {
                phase: 'authenticated',
                user: user,
                error: null
            });

        case 'AUTH-ERROR':
            return Object.assign({}, state, {
                phase: 'error',
                user: null,
                error: action.error.message
            });

        default:
            return state;
    }
}

UPDATE Dan Abramov contacted me and suggested that putting the initialState with the reducer was “the right pattern”. We don’t want to be propagating anti-patterns. As a result, I have moved the initialState to the reducer after the initial publication of this blog post. You can also use an ES6 default argument to set the initialState.

Why is this requirement for no side effects so important? You do like to test your code, right? This requirement enables the testability of the code, and that is very important.

There are definitely other ways to organize your code once your store (and state requirements) grow, and you can read about them over on the Redux web site. However, this is good enough for now. We’ve got an action creator, a set of actions that describe the manipulations to application state we want to handle, including one action that won’t actually work (we’ll get onto that later, but take a wild guess as to which one!) and a reducer that will return the new state when it is fed an action.

Step 3: Creating the Store

The next question, of course, is how do we tie all this together? Well, that’s the job of the store. Note that this is a singular entity. In my implementation of flux, I would suggest that you have different stores to handle different types of data. If you are doing a blog, you might have a store for authentication, a store for blogs, a store for comments, and so on. In Redux, there is only one store.

Here is how I implemented the store given the reducers and actions I’ve already created:

import { createStore } from 'redux';
import reducer from './reducers';

let store = createStore(reducer);

export default store;

The store is created with createStore(). The first argument is the reducer (or set of reducers if your application is more complex).

UPDATE: createStore() takes a second optional argument – the initialState. However, Dan Abramov contacted me and suggested that this was an anti-pattern in client applications. I’ve since moved the initialState to the reducer (above).

You can now use this store to subscribe to store changes and to dispatch actions. Dispatching actions that are not asynchronous is easy:

// store.dispatch(actionCreator(args));
// For example:
store.dispatch(receiveAuthenticatedAuth(response));

You’ve actually already seen this in the actions.js file I showed earlier. However, that async method is going to take something. Here is how I started with it:

import { createStore } from 'redux';
import { requestAuthInfo } from './actions';
import reducer from './reducers';

const initialState = {
    phase: 'pending',
    user: null,
    error: null
};

let store = createStore(reducer, initialState);

// Dispatch the initial action
let requestAction = requestAuthInfo();
requestAction(store.dispatch);

export default store;

It doesn’t actually dispatch an action. I’ve got a problem with that, but I’ll come back to that later. On to our components:

Step 4: Update the Component Views

I’ve got one component view – the Chrome.jsx file. There are also a couple of calls you need to know – the main one being that you can subscribe to changes and then unsubscribe later on. Let’s take a look at my Chrome.jsx – at least the important parts. First off, the constructor:

    constructor(props) {
        super(props);
        logger.entry('$constructor', props);
        this.state = {
            phase: 'pending',
            user: null,
            error: null,
            leftMenu: {
                isOpen: false
            }
        };
        logger.debug('state = ', this.state);
        logger.exit('$constructor');
    }

Note that the state includes all my store variables. This actually is fairly important at this point. It will become less so later on. Now, onto the component state lifecyle functions:

    /**
     * React API: Called when the component is mounting itself in the DOM
     *
     * @returns {void}
     * @overrides React.Component#componentWillMount
     */
    componentWillMount() {
        logger.entry('componentWillMount');
        this.unsubscribe = store.subscribe(() => { return this.updateState(); });
        logger.exit('componentWillMount');
    }

    /**
     * React API: Called when the component is removed from the DOM
     *
     * @returns {void}
     * @overrides React.Component#componentWillUnmount
     */
    componentWillUnmount() {
        logger.entry('componentWillUnmount');
        this.unsubscribe();
        logger.exit('componentWillUnmount');
    }

    /**
     * Update the internal state of the component-view from the flux store
     */
    updateState() {
        logger.entry('updateState');
        this.setState(store.getState());
        logger.debug('New State = ', this.state);
        logger.exit('updateState');
    }

This is pretty much standard stuff for flux. You register your interest in the componentWillMount() method and then deregister in the componentWillUnmount() method. In the updateState() method, I merge the stores state with the components state which will then re-render the DOM.

That’s pretty much all there is to redux. Instead of a complicated function in a store, the store is completely separated and reducers take over the task of actually doing the state transitions. Otherwise, this is as vanilla Redux as you can get. Sure, it’s a little more modular, and smaller (141 lines of code instead of 214 for my store implementation), but…

The only real advantage is that I’m not writing the store myself.

However, there is much more to Redux than what we’ve done thus far. In the next post, I’m going to take a look at two of those things. Firstly, I am going to simplify my component view to get rid of a lot of the boiler-plate code for handling state updates. Secondly, I’m going to look at the role of middleware to handle async functions. Until then, check out the code in my GitHub Repository.