In the last installment of this series, I wrote about the basics of immutable.js. Today, I want to write about how to use it together with Redux.

I wrote a very small application with React, Redux and immutable.js: It just displays two red squares and some lines to the corners of each square. And you can drag and drop the corners with your mouse.

Simple React / Redux App

This functionality may sound simplistic. But it has some react components that operate on common data that can change (both Link and Square need the location of a Square), and so it can potentially benefit from Redux.

You can find the full source code here. For now I am showing you the code from the tag simple-redux-immutable.

Reducers and Immutable

There are two things you have to do to make your reducer work with an immutable data structure:

Make the initial state an immutable object

import { fromJS } from 'immutable';

const initialState = fromJS({
    squares: [
        { x: 316, y: 281, },
        //...
    ],
    edges: [
        { x: 0,    y: 0,   squareX: 0,  squareY: 0,  },
        //...
    ],
});

export function reducer(state = initialState, action) {
    //...
    return state;
}

src/Reducer.js

This application draws squares and lines between them. The data required to do so is stored in an immutable map with the keys squares and edges. Each of those keys contains an immutable list of Maps, that in turn contain the data.

Instead of creating a new JavaScript object, perform an immutable update

export function reducer(state = initialState, action) {
    switch(action.type) {
        case 'SQUARE_MOVED':
            return state
                .updateIn(['squares', action.id, 'x'], x => x+action.dx)
                .updateIn(['squares', action.id, 'y'], y => y+action.dy);
        }
    return state;
}

src/Reducer.js

When the user drags a square with their mouse (action SQUARE_MOVED), the reducer updates the x and y coordinate of that square.

mapStateToProps

In mapStateToProps, I get the data required for rendering the components from the redux store. Either as simple JavaScript numbers:

function mapStateToProps(state, ownProps) {
    return {
        x: state.getIn(['squares', ownProps.id, 'x']),
        y: state.getIn(['squares', ownProps.id, 'y']),
    };
}

src/Square.js

Where ID is provided by the parent component (src/DrawingArea.js):

<SquareContainer id={0} />

Or as an immutable data structure, that the component itself will pick further apart:

function mapStateToProps(state, ownProps) {
    return {
        square: state.getIn(['squares', ownProps.toSquare]),
        edge: state.getIn(['edges', ownProps.fromEdge]),
    }
}

src/Link.js

Where toSquare and fromEdge are again provided by the parent component (src/DrawingArea.js):

<LinkContainer fromEdge={0} toSquare={0} />

PureComponent

With redux, all the components that actually render stuff - Your Presentational Components - can be pure components (at least when you stick to the rules of Redux):

export class Square extends React.PureComponent {
    //...
}

src/Square.js

This may or may not be faster than having “normal” react components. What is more important to me is: It serves as a reminder to keep this component side-effect-free.

And to not implement shouldComponentUpdate: A presentational component that gets its data from redux should:

  • Not have any internal state
  • Only get the data it absolutely requires in its props, and thus:
  • Always re-render when the props change

Rendering

When the component only gets plain values in its props (like the numbers x and y for Square), then render looks as if there would be no immutable.js at all. But when the component gets immutable data structures through its props, like Link, it can use them in its render function (and elsewhere):

render() {
    const fromX = this.props.edge.get('x');
    const fromY = this.props.edge.get('y');

    const toX = this.props.square.get('x') + this.props.edge.get('squareX');
    const toY = this.props.square.get('y') + this.props.edge.get('squareY');

    return (
        <path d={'M '+fromX+' '+fromY+' L '+toX+' '+toY+' z'} className="link" />
    );
}

src/Link.js

Updates / Actions

On a DOM event, the component creates an action. There is no difference to not using immutable.js here. In this case, to handle mouse events correctly, the code first gets a ref to the DOM node and then installs the listener in componentDidMount:

<rect x={this.props.x} y={this.props.y} 
      width={50} height={50} className="square" 
      ref={e => this.rect=e} />

src/Square.js

componentDidMount() {
    this.rect.addEventListener('mousedown', this._mousedown_bound);
}

src/Square.js

Most of the code in the mouse listeners is for handling dragging with the mouse in a way that it does not jitter. But on mouse move, the component also calls an action creator:

_mousemove(e) {
    //...
    this.props.squareMoved(this.props.id, e.movementX, e.movementY);
}

src/Square.js

Which only packs its three arguments into an action object of type SQUARE_MOVED. When the reducer handles this action, it must use immutable updates. Note how it calls the second updateIn on the result of the first and how it returns the result of that:

case 'SQUARE_MOVED':
    return state
        .updateIn(['squares', action.id, 'x'], x => x+action.dx)
        .updateIn(['squares', action.id, 'y'], y => y+action.dy);
}

src/Reducer.js

CombineReducers

Redux only knows one reducer. But being forced to write a single, huge reducer would lead to unmanagable code. Hence you should split your reducer code and combine the smaller reducers with combineReducer.

And so, now I want to show you how you can structure your reducers when using immutable.js. Switch to the tag immutable-combine-reducers to see the source code of this version.

The only difference here is that you have to use combineReducers from redux-immutable instead of the default one:

import { combineReducers } from 'redux-immutable';

export const reducer = combineReducers({
    squares: squaresReducer,
    edges: edgesReducer,
});

const edgesInitialState = fromJS([
    { x: 0,    y: 0,   squareX: 0,  squareY: 0,  },
    //...
]);

function edgesReducer(state = edgesInitialState, action) {
    return state
}

const squaresInitialState = fromJS([
    { x: 316, y: 281, },
    //...
]);

function squaresReducer(state = squaresInitialState, action) {
    switch(action.type) {
        case 'SQUARE_MOVED':
            return state
                .updateIn([action.id, 'x'], x => x+action.dx)
                .updateIn([action.id, 'y'], y => y+action.dy);
        }
    return state;
}

src/Reducer.js

To Recap…

And that’s it: To use immutable.js within your React / Redux app, you have to:

  • Update your reducer’s initial state to immutable data structures
  • Use the immutable data structures in your mapStateToProps and possibly within your presentational components
  • Combine your reducers with a different function

You should also make all your presentational components PureComponents.

And then you will automatically get the advantages of immutable.js: It ensures by default that you have immutable state in your redux store and it does updates in a very efficient way.