Introduction to Redux without todo example

It’s been a while since the latest post, but it is time to introduce Redux. You won’t find a straightforward implementation of a todo example here, I rather apply Redux to our Silly App we created in the last post.

Download and setup

The code for this post can be found on Bitbucket, make sure to checkout the version with tag 1.3. What I found useful here is to just download the specific version of the repository and extract the zipfile in the directory where I want to setup the project.

Then it is time to run npm install from the command line in the directory where we just put the code. All dependencies will be downloaded from npm and after this has been completed we can run npm start. This will start up the Express dev server so we can run the app locally on http://localhost:7770.

And there we have it, the Silly App is back on the screen! Like before we have a starting screen with two buttons leading to either a useless view with some lorem ipsum text or to a dynamic page where the user can click on a button to change the styles of the displayed text.

But the interesting thing is the code. In the previous post we kept the state inside of React but when an application grows and becomes more complex it is better to separate the views and the state from each other. This is where Redux comes into play.

Introduction to Redux

Redux logoTraditionally in React we used the onClick attribute on a component. Clicking a button would pass control upwards to change the state and after calling setState all views would be updated.

With Redux this is different. Clicking a button now dispatches (a.k.a. triggers) an action. This action, basically a simple JS object which describes what happens, is passed to a reducer, a function that will take the current state and the action and returns the new state to the store. The store will listen to state changes and pass this new state to the views in the application and they will rerender themselves.

And how does it work in code?

This probably sounds a bit theoretical. It is, but I think it is the power of Redux to have a clear way of doing things. The communication in the app is always in one direction, i.e., from the user doing something and dispatching an action towards the reducer that comes up with the new state and the store passing it to the views that update. If you fancy diagrams more than words, I shamelessly took this image showing the flow from Gerard Sans.

Redux flow

But let’s proceed with the Silly App to see how we incorporate Redux in the app. Consider the following steps:

  1. Define and implement actions
  2. Define and implement a reducer
  3. Initialize the store
  4. Connect Redux to the React app   // Will be treated next time

In the first three steps we don’t bother about how things will come together, this is discussed in the last step which we’ll cover in the next post to keep this post relatively short

Actions

As said before, an action is simply a JS object describing what the action does (in its ‘type’) and if needed some data that is send along. For our Silly App we have one action which will be triggered when the user clicks the button saying ‘Click me!’ in the Clickable view.

The action looks like this:

let action = {
    type: 'TOGGLE_VIEW'
};

It’s type is ‘TOGGLE_VIEW’ which is essentially what happens when clicking the button. Note that you are absolutely free to choose whatever type you need for your application. Also it is only recommended to use uppercase ketters, not mandatory.

To connect Redux to React later on, the object is wrapped inside a function, the so called action creator. The action creator is this function:

export const toggleView = function() {
    return {type: 'TOGGLE_VIEW'}
};

which basically just returns the object once called.

Reducers

As the user interacts with the app, an action is dispatched. This means the store will call the reducer function with two arguments: the current state and the action. So far I haven’t mentioned the state of our application but it is a simple object that is initialized like this:

const initState = {
    count: 0,
    view: 0
};

It simply is an object with an integer to count the number of clicks on the button and a boolean ‘view’ which toggles the correct styling for the Clickable component.

Given the specific action we have here, TOGGLE_VIEW what needs to happen to the state when this action is dispatched? First we need to increment the counter and next we need to change the boolean value. Hence the reducer function reads

const appReducer = function(state = initState, action) {
  const {count, view} = state;
  switch (action.type) {
    case 'TOGGLE_VIEW':
      return {
        ...state,
        view: 1 - view,
        count: count + 1
      };
    default:
      return state'
  }
}

This perhaps needs some explanation. First we pass the initState as initial state. In all subsequent calls to the reducer the state is available but the first time we need to use initState which was defined above.

On line 2 we extract the variables count and view from the state using the destructuring syntax new in ES6.

Then we make a switch. For our current application this is not mandatory because there is only one action but we have to make this scalable! A new state should be returned and this is achieved by returning an object containing …state and the updated values for view and count.

Now this looks a bit funny, doesn’t it? Those three dots…

It is the object spread operator which also shipped with ES6. It makes sure that we use all information in the old state. Updated keys are then added to the object. Here we only have two keys in the state that both change, but consider a state containing much more data, this is very useful as we don’t have to type all the old values!

Finally to play safe, we add a default in the switch assignment which just returns the old state.

When the reducer is called with the action just created, it increments the count and changes the view.

One more thing on reducers though. In the reducer.js file in the code on Bitbucket you see another reducer called the rootReducer. This is the one overall reducer that is passed to the store. Using the combineReducers utility function in Redux it is possible to split your reducer in smaller and simpler functions that each take care of a particular part of the state. In our example we are doing this silently. We are using React Router which also comes with its own reducer handling the switching of URLs. Hence, for the UI part we use the appReducer and for the routing part the routerReducer. These two are combined in the rootReducer.

Store

The store is actually the thing that holds the state and dispatches action to the reducer. The store also listens to the reducer and rerenders the applcation when a new state is available. In the most simple setup we just call createStore and pass it the rootReducer but in the code we apply additional middlewares. I am not going into detail of middlewares in this post but we need them here for logging the actions and results to the console (nice for debugging) and for making the browser’s back button working.

Concluding remarks

I can imagine that the whole concept behind Redux needs some time to land. So I’ll end this post here and we continue in the next post to connect Redux to the React app. In the meantime you could have a look at the excellent Redux documentation!


Mijn Twitter profiel Mijn Facebook profiel
Pim Hooghiemstra Webdeveloper and founder of PLint-sites. Loves to build complex webapplications using React! Latest post
Testing Vue components in an isolated way using Storybook

Leave a Reply

Your email address will not be published. Required fields are marked *