React Redux - A Flash Messenger Example

A React/Redux app can suffer from a steep learning curve and a byzantine tangle of architecture. There is a lot of boilerplate overhead, and the linkages between elements can be a little opaque. But once you get things going, React and Redux offer a lot of power and an incredible amount of clarity. Redux architecture is based around a strict single source of truth held in the application state. All components tapping into that state are automatically updated with any changes dispatched from anywhere else in the application.

To illustrate these advantages, I'll go through how to set up a simple Flash Message component that will receive and display messages dispatched from anywhere in the application.

If you want to see the full code, checkout the project on Github.

App Structure

Create a new directory and get an npm init going. Install the following packages:

react  
react-dom  
redux  
react-redux  

Make sure you have your build system set up using Webpack and Babel. If you would like a quick run down on that, check this out.

Our app files will live in the src directory and be compiled into the dist directory. Here's a look at the application structure we'll be using.

├── dist
│   ├── bundle.js
│   └── bundle.js.map
├── index.html
├── package.json
├── src
│   ├── action
│   │   └── index.js
│   ├── component
│   │   ├── app.js
│   │   ├── flash_message.js
│   │   └── user_action.js
│   ├── index.js
│   └── reducer
│       ├── index.js
│       └── reducer_message.js
└── webpack.config.js

Writing Our Action

Actions are the verbs in the Redux vocabulary. They allow components to dispatch state changes throughout the rest of your application. The structure of a generic action in Redux is a function that returns an object with two properties, a type string and some kind of payload. The type property allows the reducers, which receive all actions, to distinguish between actions by matching the string names, while the payload is the data being transmitted.

We will have a single action, sendFlashMessage which will be a function accepting two parameters, a className and a message. It will return an object of type 'FLASH_MESSAGE' with a payload corresponding to the passed in message and className.

// action/index.js

export const FLASH_MESSAGE = 'FLASH_MESSAGE';

export const sendFlashMessage = (message, className) => {

  return {
    type: FLASH_MESSAGE,
    payload: {
      message,
      className
    }
  }
};

Notice the FLASH_MESSAGE variable exported at the top. This is a common convention with Redux apps as exported variables will let you know when you have a typo instead of trying to match strings as we'll see later when we set up our reducer.

Set Up A Reducer

Reducers are the great state managers in a Redux application. They receive all the actions and take appropriate steps to modify the application state accordingly. Reducers are functions that accept two parameters, state and action. state being the application state and action the current action being passed through. They get their name from the reduce function where state is the memo object, and action is the action currently being applied.

Reducers must follow a few hard and fast rules: First, they must be pure functions and second, they may not modify the existing scope, but instead return a modified copy. They must also always return something, generally letting the state pass through unmodified by default. If a reducer doesn't return anything, the state will be lost when it passes through.

Dive in more at the Redux reducer docs.

When designing reducers, it's useful to design your application state first. Ours will look like this:

{
  flashMessage: {
    message: 'message text',
    className: 'className to apply to message display'
  }
}

Although our app is exceedingly simple, I'll make use of the combineReducers function from the react-redux library. This will map a list of reducers to a particular property on the state object. This practice is common in larger Redux apps as it easily allows numerous reducers split across multiple files to deal only with particular pieces of application state.

// reducer/index.js
import {combineReducers} from 'redux';

import messageReducer from './reducer_message';

const rootReducer = combineReducers({  
  flashMessage: messageReducer
});

export default rootReducer;  

Our reducer will look out for actions with type FLASH_MESSAGE and will pass along the action payload as the value of the flashMessage state property. To acheive this, reducers commonly contain switch functions to check for the appropriate action.type. Our reducer will also create a default value for the flashMessage property and will in the default case, return it.

// reducer reducer_message.js
import {FLASH_MESSAGE} from '../action/index';

const initialState = {  
  message: null,
  className: null
}

export default (state = initialState, action) => {  
  switch(action.type){
    case FLASH_MESSAGE:
      return action.payload;
    default:
      return state;
  }
};

Setting Up Our Application

Our app will consist of three components to illustrate how to dispatch actions and how to consume application state changes.

The first is the FlashMessage component which will watch for state changes and will display any flash messages sent. To set this up, we'll use another function from the react-redux library, connect. This function will help us map properties on the application state to local props on the component and so will trigger the render function to display any updates.

The first argument to connect is a function that receives the application state as its parameter. It should return an object corresponding to all props that should be placed on the current component. Finally, we'll call connect with the appropriate arguments and export the result as our component.

// component/flash_message.js

import React, {Component} from 'react';  
import {connect} from 'react-redux';

class FlashMessage extends Component{

  render(){
    const {message, className} = this.props.flashMessage;
    if(!message){
      return null;
    }

    return (
      <div className="row">
        <div 
        className={'col-md-12 alert ' + className} 
        role="alert">
          {message}
        </div>
      </div>
    );
  }
}

const mapStateToProps = ({flashMessage}) => {  
  return {flashMessage};
};

export default connect(mapStateToProps)(FlashMessage);  

Our next component will simulate user interactions and will dispatch actions to our reducer. We will once again use the connect method to make the sendFlashMessage action we created in action/index.js available to our component. In addition, we also need to use the bindActionCreators function from redux which will allow the action to be appropriately dispatched to the reducers.

connect takes a second function as an argument which will receive the dispatch function as a parameter. We will pass bindActionCreators two arguments: an object containing all the actions we want to place on the props of the current component, and the dispatch function. With that taken care of, we can use the action as we normally would through out the component.

// component use_action.js

import React, {Component} from 'react';  
import {connect} from 'react-redux';  
import {bindActionCreators} from 'redux';

import {sendFlashMessage} from '../action/index';

class UserAction extends Component{

  render(){
    return (
      <div className="row">

          <div className="col-md-8 col-md-offset-2">
            <div className="btn-group btn-group-justified" role="group">
              <div className="btn-group" role="group">
                <button
                onClick={() => this.props.sendFlashMessage('You win!', 'alert-success')} 
                className="btn btn-success">Happy Message</button>
              </div>
              <div className="btn-group" role="group">
                <button
                onClick={() => this.props.sendFlashMessage('You\'ve been warned', 'alert-warning')} 
                className="btn btn-warning">Warning</button>
              </div>
              <div className="btn-group" role="group">
                <button
                onClick={() => this.props.sendFlashMessage('Way to go...', 'alert-danger')} 
                className="btn btn-danger">You Blew It</button>
              </div>
            </div>
          </div>

      </div>
    );
  }
}

const mapPropsToDispatch = (dispatch) => {  
  return bindActionCreators({sendFlashMessage}, dispatch);
};

export default connect(null, mapPropsToDispatch)(UserAction);  

Next, we'll tie these components together in a top level app component.

// component/app.js

import React from 'react';

import FlashMessage from './flash_message';  
import UserAction from './user_action';

const App = () => {  
  return (
    <div className="container">
      <FlashMessage />
      <hr/>
      <UserAction />
    </div>
  );
};

export default App;  

Setting Up Redux Store

The last piece is the Redux store. Actions make things happen, reducers interpret the results of actions and update the state accordingly, but the store is the place that holds everything together. The store holds onto the application state and allows actions to be dispatched to update that state.

We'll set up the store in index.js, the entry point of our application. To create our application store, we'll use the createStore function from redux. All we'll do is pass our rootReducer, as export from reducer/index.js into the createStore function. We'll also need to make use of the Provider component from the react-redux library so that all of our connect() calls in our components have access to the store.

// index.js

import React from 'react';  
import {render} from 'react-dom';  
import {Provider} from 'react-redux';  
import {createStore} from 'redux';

import rootReducer from './reducer/index';  
import App from './component/app';

render(  
  <Provider store={createStore(rootReducer)}>
    <App />
  </Provider>,
  document.getElementById('container')
);

Getting Reduced

OK, great! We have an action that gets dispatched to the reducers which updates the state which is held in the store which updates our components which displays it on the page! Wouldn't that example have been way easier with jQuery? Of course as this example is understandably trivial, but the real power of React and Redux comes in the fact that we could place those actions anywhere in the app. They could be called in deeply nested components and seamlessly be displayed anywhere in our application. We could make any number of components have access to the flashMessage property on state, and they would all work as expected. Redux' single source of truth means that our application state is in perpetual sync and the unified store means that updates in any component get transmitted to any and all components that are interested without having to pass down increasingly long chains of parent and child props and callbacks. Although there is a lot of overhead and some rather unique logic to wrap your head around initially Redux offers a very elegant solution to managing state in your lovely React apps.

Again, check out the full project code here.