Managing your React state with Redux

In this post we will take a look at using Redux for managing the state in React apps. Redux is a very simple library that enables predictable atomic state changes. While the API is tiny and easy to learn, it gives you a lot of power and solves many of the problems when handling the application state and sharing data between components.

The fact that the Redux state changes predictably opens up a lot of debugging possibilities. For example, using time travel makes it possible to travel back and forth between different states instead of having to reload the whole app in order to get back to the same place.

To learn how to integrate the Redux DevTools in your app to enable time travel, please take a look at this article:

The code for this article is available in this GitHub repo. It’s simple drawing application where the user can toggle cells in a grid by clicking on them.

You can play with it here:

https://argelius.github.io/react-redux-timetravel/

The bar on the right is the Redux DevTools which enables simple debugging inside the app itself. It is also available as an extension for Google Chrome. To learn how to integrate the DevTools, please click here.

I have also created a larger example that uses React and Redux with Onsen UI. It’s a simple weather forecast application. The code for that application can be found here and there is also a demo available. We will take a look at how this app is built in a coming blog post.

What is Redux?

Patrick already wrote a great article on Redux so I will keep this short. Redux is a state container for JavaScript apps, often called a Redux store. It stores the whole state of the app in an immutable object tree.

To create a store the createStore(reducer, [initialState], [enhancer]) function is used to create a new store. It takes three arguments:

  • reducer - A reducing function. We will describe it below.
  • initialState - The initial state of the store.
  • enhancer - Can be used to enhance the Redux store and add third-party libraries and middleware for logging, persistant storage, etc.

In the app described below we use the redux-logger library to add console logs when the state changes. This is done with the applyMiddleware function:

The Redux store API is tiny and has only four methods:

  • store.getState() - Returns the current state object tree.
  • store.dispatch(action) - Dispatch an action to change the state.
  • store.subscribe(listener) - Listen to changes in the state tree.
  • store.replaceReducer(nextReducer) - Replaces the current reducer with another. This method is used in advanced use cases such as code splitting.

The state can only be changed by emitting an action. The state tree is never mutated directly instead you use pure functions called reducers. A reducer takes the current state tree and an action as arguments and returns the resulting state tree.

The following is an example of a simple reducer:

I am using the spread operator in the code. This basically makes it possible to copy an array by doing:

const copy = [...original];

This is important because we want the reducer to be a pure function that doesn’t mutate the state directly. So instead of pushing a new element to the array we create a brand new array containing all the previous state in addition to the new element.

As seen in the code above, Redux is a very simple library but because it’s deterministic (the current state is only dependent on the previous actions) it makes the behaviour of the app very predictable and less prone to bugs. It also enables time travel which is an efficient way of finding and solving bugs should they occur.

In the example above there is only one reducer with a single action acting on the state tree but adding new actions and combining several reducers is a relatively simple task. Redux scales very well for larger applications given that the code is structured in a good way.

Redux with React

Redux is in itself not written specifically for React. It can be used with other frameworks such as Angular, Ember or Vue.js. However, Redux was written with React in mind and they work very well together. For a guide on how to use Redux with Angular2, please take a look at this article.

The easiest way to use Redux with React is the official React Redux binding library. With this library it’s easy to bind the Redux state and actions to props.

I have created a simple drawing application to illustrate how it can be used. The code is available in this repo on GitHub. I have used a project structure where I split reducers and action creators into different directories. This is a common way to structure the code and it’s what used in the official Redux examples. However, there are other options available such as Ducks where the reducers and action creators are bundled together.

In this project Webpack is used to create the bundle.js file. The Webpack config is available here. Webpack is not the only bundler out there, the code below could just as well be bundled using Browserify or Rollup.

Running the example is as easy as doing:

git clone git@github.com:argelius/react-redux-timetravel.git 
cd react-redux-timetravel
npm install
npm start # This will start a server at http://0.0.0.0:9000/

The Redux part of the code is separated into two parts, action creators and reducers. The reducers have already been explained. An action creator is a simple function that returns the object that should be dispatched in order to execute an action. As an example an action creator for the ADD_QUOTE action in the example above can be written like this:

The syntax {text, person} is shorthand for {text: text, person: person} introduced in ES2016.

To dispatch the action we can now do:

store.dispatch(addQuote('I shook up the world!', 'Muhammad Ali'));

Let’s take a look at the code of index.js, the entry point of the app. It contains a lot of boilerplate code to enable hot reloading of components and to initialize the Redux DevTools. We will discuss the DevTools in the next blog post so I will not go into the details.

The important part is the Provider component which makes the Redux store available to all its descendants. Without this component you would have to pass the store as a prop to all the components that need it.

I have put some comments to clarify what the code does.

Action creators

An action creator is a function that returns an object representing an action that can be dispatched to the Redux store. In this app we have two actions:

  • SET_GRID_SIZE - Change the size of the grid.
  • TOGGLE_CELL - Toggle if a cell is filled or not.

These objects become arguments to the reducer when dispatched.

Reducers

This app only has one reducer, called grid. The state has the following form:

{
width: 10,
height: 10,
cells: [1, 0, 1, 1, ...]
}

The width and height parameters are self-explanatory. The cells array is a list of 1s and 0s that represent if a cell is filled or not.

The implementation is pretty straightforward. However, when changing the size of the grid we need to be careful so the previous picture is copied correctly onto the new grid since the dimensions changes.

As mentioned before, the reducer should be a pure function which means that we need to copy the cell array before returning the next state.

Components and containers

In apps using React Redux the components are often separated into two categories, components and containers. This is to distinguish components that use the state tree and dispatch actions from dumb components that are only used for presentation.

This app has three dumb components:

  • App - The root of the app.
  • Row - A row in the grid.
  • Cell - A grid cell.

Since these components are only used for presentation the implementation is pretty simple. This is one of the strengths of Redux, by separating the state from the graphical presentation we can make tiny reusable components.

The app also has two containers (actually three but we will discuss the DevTools container in the next blog post).

The two containers are:

  • Grid - Renders a grid of Row and Cell components based on the state and dispatches actions when the cells are clicked.
  • SetSize - Renders two input elements that can be used to changed the size of the grid.

App component

The App component just renders two child components: Grid which is the clickable grid and SetSize which is used to set the size of the grid.

Row component

The Row component is very simple and basically just contains the flexbox layout to make the cells align horizontally and stop them from wrapping.

Cell component

The Cell component is a clickable cell and it has a black or white background based on the value of the filled prop.

Connecting components to Redux

We will now take a look at the containers but first we need to understand the connect() method of the Redux React library.

The connect() method takes two arguments: mapStateToProps and mapDispatchToProps and returns a function that can be used to connect the Redux store with a component.

Let’s take a look at how this can work with an example:

Grid container

Given this we can now take a look at the Grid container and understand how it works. To render the grid it loops through all the cells and renders them either black or white depending on the state:

SetSize container

Conclusion

Redux is a great library for managing the state of React apps. Storing the whole state in a single tree is very useful since it avoids having multiple sources of truth.

In the coming days we will release a couple of blog posts about the Redux DevTools and a tutorial on how to create a more complex app using React Redux and Onsen UI. You can check out the app here.

We hope the article has been interesting and if you have any questions don’t hesitate to ask in the comments below. If you are a hybrid app developer or thinking about getting into mobile app development, please check out our Onsen UI library and give us a star if you like it. :)