After the first part of our Beginner’s Guide to Redux, we should have a good understanding of what we want to accomplish, as well as an idea of how we want to implement it within our application. So instead of going even deeper into Redux philosophy, why not spend this part focusing on the code?

In these next 2 parts, we are going to do more than look at one way of using Redux with our application, and instead we are going to observe several ways to use Redux for the same application. Realistically we need to see the various scenarios we will certainly run across when using Redux, so let’s get to coding!

Part 1: Beginner’s Guide to Redux – About Redux

The Plan Moving Forward

What you can expect from here is turning a simple React boiler-plate application into a fully functional React-Redux application. Like we did in Part 1, the application will take advantage of the standard Redux scaffolding folder/naming scheme. There are a couple reasons for this, one you learn what each part of redux is, and know exactly where to place it. For those planning to use React-on-Rails, you will find yourself faced with this exact scaffolding schema, but you as well as others will understand more than enough to move on to your own isomophic setup if you prefer something less folder intensive.

To get started, go ahead and clone the following repository:

Pre-Redux React App and Boilerplate

Before we start, let us discuss a few concepts we need to keep in mind when using Redux. First, our state will be immutable, so if we find ourselves changing values manually in any part of the redux flow, there is a better and more optimized to accomplish this task. Second, our logic within redux is centralized, there is no reason for it to ever be separate from neighboring code; working this way will allow us to scale the app with ease as well as make our code very easy to debug. Third, our react components should follow the convention of Smart, and Dumb Components leaving the job of interacting with our state (Redux) to our Smart Components, and our presentation to the Dumb Components. You can read more about this practice in Dan Abramov’s article on components.

Lastly, we will not skim over the specifics of redux in any way, so let’s get to it, there is a lot to cover!

Redux Actions

branch: 01-actions

For those following along, go ahead and switch to the branch listed under the section name in the git repository.

The very first thing we want to abstract out of the test app are the actions we use within the app. When we add them to the components themselves, we end up with extremely long components with lots of methods. Moving to Redux these actions will instead reside in the Actions folder, and within its own namespace.

We certainly do not want all of our actions in one file moving the problem to a different folder, so we classify our actions based on what the these actions actually control. Right now we will only focus on the Counter actions so we will actually make the namespace Counter. To give a home to these actions we create a new file in the Actions folder named CounterActions.js, and put the actions in exported functions:

import * as types from './ActionTypes'

export function counterUp() {
  return {type: types.COUNTER_UP}
}

export function counterDown() {
  return {type: types.COUNTER_DOWN}
}

As you can see our actions are a bit different, and dubbed “Action Creators”, but they will still accomplish the same task. Instead of our actions directly changing the state, we will return an object with the action we are calling. This is the common pattern for Synchronous actions in Redux since we do not need to wait for a response, and know that everything needed is passed on.

Redux Actions

We now need to address the import we have in the actions file which is actually nothing more than a list of our actions in our Redux app. So, we create another file in the Actions folder called ActionTypes.js with the following code:

 // counter
export const COUNTER_UP = 'COUNTER_UP';
export const COUNTER_DOWN = 'COUNTER_DOWN';

As you can see there isn’t much to it and all we are declaring is a string to a constant. In our action file we are calling these constants, and in turn passing a string in our object. We could pass a string*, but this is actually a great way to keep track of your actions, as well as an excellent place to take inventory on actions when planning out their creation. Now that we have that out of the way our action creators are good to go.

*we will discuss other ways to deal with action types later

With the actions ready it is time to plug these actions into the application itself. We go back to where our actions came from, our App.js file, and start by removing the old methods. To infuse redux we need to add the necessary modules below the React import:

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

Ok great, so how do we access our actions? There are a couple different ways, and one to at least mention is that we now have a dispatch action attached to our future store. With this dispatch action we can call our action creators from inside the actions folder using the following command:

store.dispatch(actions.actionName(data))

The only issue is that this can get very repetitive, and there is a much better way to go about it. Instead we will use the power of React Redux to attach our actions to the props themselves. Let’s first give our App file access to our actions by importing all of them:

 import * as CounterActions from '../actions/CounterActions'

Now it is time to attach our actions to props and adding the following after the App class:

 class App extends Component {
...
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(CounterActions, dispatch)
  }
}

export default connect(mapDispatchToProps)(App);

Also note that the export has been removed from the class itself, and added to the new connect default export. With our actions now attached to props, we can access any of our Counter Actions with this.props.actions.actionName.

For now that is it for actions, because now we need to actually hook these up so they can actually do something.

Redux Reducers (Where the Action Happens)

branch: 02-reducers

Now that the actions are out of the way, we need something for our actions to actually interact with. This specific component is called the Reducer. The reducer handles our state, as well as acts as a listener for all of the actions called to change state. So now it is time to start adding some listeners so our actions’ commands can stop going unheard.

Redux Reducers

Starting off, all of our reducer logic will live within our Reducers folder, and to get us started we will create a new file called rootReducer.js:

 import { combineReducers } from 'redux'
import CounterReducer from './CounterReducer'

const rootReducer = combineReducers({
  counter: CounterReducer
})

export default rootReducer

As you can see the root is pretty boring, and for good reason. The idea here is to have a single file that handles all of our other reducers, and as you can see we already included our non-existent CounterReducer. Since we do not directly interact with this file, I like to make the first letter in the file lower-case. Our root reducer takes all of the reducers and combines them together to pass to the next portion of redux to handle the state. Before we dive into that, we need to create our CounterReducer.js file:

 import * as types from '../actions/ActionTypes'

let total = 0

export default function counterReducer(state = {total}, action) {
  switch(action.type) {
    case types.COUNTER_UP:
      return {
        total: state.total + 1
      }
    case types.COUNTER_DOWN:
      return {
        total: state.total - 1
      }
    default:
      return state;
  }
}

Already we can see that the actual reducer has a lot more going on. We are taking in two parameters, the state, which is set to an object by default, and an action. The switch statement is based on the action parameter’s type, and as you may have noticed activates when it gets one of the action type constants. You may have already guessed it, but right here is one of our action listeners.

This may seem very basic, and in a sense it is, but the reducers are filled with more magic than you think. Since these can be created with scalability in mind, we could have dozens of reducers, and it will have no issues knowing what action to call. One of my favorite parts of reducers is that they are not restricted to their namespaces. Let me give you an example, say we send one of our CounterAction types, but the action resides in a different namespace? Well, no worries, because the reducer listening with that action will continue to fire.

Passing actions across namespaces is a favorite of mine because it allows me to create a single action for generic events like my error notifications. No matter where I call it from it will always act the same. When the app encounters an error it will pass the error from any namespace to the frontend, and trigger the pop-up notification with the error every time. We will see this is action later.

Besides receiving actions the reducers are also the gatekeepers to our state. You will notice that in each instance we are returning an object, and each object contains our new updated state. Of course you may wonder why are we breaking the immutable rule, aren’t we just changing the object? Yes, we are, and don’t worry we’ll get into that a little later.

Let’s keep all of that in mind, and move on to our new store.

03 The Lonely Redux Store

branch: 03-store 

With the actions and reducer ready to handle actions, you may have noticed we don’t have a location that actually holds our state. One of the bigger jobs the store does is keeping track of our state, as well as handling our reducers which ask for these state changes.

Of course that is not a full list of what the store does, as you find yourself in more complex uses of redux, something we will cover in more intermediate uses, you will find yourself doing the jobs of the reducer, and action creators directly from the store. In the meantime let us go ahead and create our store so we can start to see what it does for us in our immediate need.

 import { createStore } from 'redux'

import rootReducer from '../reducers/rootReducer'

const configureStore = () => {

  const store = createStore(
    rootReducer
  );

  return store
}

export default configureStore

With the configureStore file ready to go, we pretty much have a complete redux setup, but we still need to connect it to React. Starting out, we need to visit our index file, and connect the store to the rest of the app.

import style from './style/style.scss'
import React from 'react'
import { render } from 'react-dom'
import configureStore from './store/ConfigureStore'
import { Provider } from 'react-redux'
import App from './components/App'
import ErrorBoundary from './components/ErrorBoundary'

const store = configureStore()

render(
  <ErrorBoundary>
    <Provider store={store}>
      <App />
    </Provider>
  </ErrorBoundary>,
  document.getElementById('app')
);

What we have done here is wrapped our App with the Provider component giving our app access to the Redux store. While this may not seem like much, this is an extremely important component. Since the store acts as the central point of contact for anything redux, the store is a very big deal, and we will see this very soon.

To get our state integrated we need to create a new file in our reducers folder called initialState.js, and this file will act as the default state values when the app opens.

 export default {
  total: 0,
  title: 'Our Redux App'
}

There isn’t a whole lot going on here, but as it grows you will see that it is quite helpful to contain your default state into a single file. Of course we now need to connect redux to this state, and then we can start to make use of it.

In the previous section we hooked the reducer to a single object to act as our state, but now that we have access to the store we can connect to it instead.

 import * as types from '../actions/ActionTypes'
import initialState from './initialState';

export default function counterReducer(state = initialState, action) {
  ...

So now we have the store integrated throughout our app, but there is one last thing to do to make our Redux implementation operational, and that is to connect it to React.

To connect our component to Redux we need to make use of react-redux, as well as a helper from redux itself. We need to import these into our App.js file

With redux connected to our component, we now need to connect our state to the component similarly to how we connected our actions. We can do this with a new function called mapStateToProps which will be added next to our mapDispatchToProps in the App.js file:

 function mapStateToProps(state) {
  return {
    state: state
  }
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(CounterActions, dispatch)
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App);

With the state connected to the component, we no longer need our component’s state for our counter, so why don’t we go ahead and switch our state calls to our redux store? Thanks to our mapStateToProps we can now access our state with this.props.state. So any call to this.state.total, or this.state.title can be switched to this.props.state.total, and this.props.state.title.

 render() {
    return (
      

{this.props.state.title}

{this.props.state.total}

Excellent! With the counter actions and state connected to redux, we can remove the old methods and state from App.js cleaning up our component. Of course we will get to the user actions later, but for now let’s fire up the app, and test this out.

If you have fired this up, and clicked the counter buttons, you may have noticed nothing is happening. Also, if you have the React extension installed to your browser, you will also notice the actions are in fact working. So what’s going on?

Personally I thought this would be a perfect opportunity to sneak a bug into the code, especially right before we learn more about making our store more robust, and adding Redux DevTools where we can debug such issues.

04 Revamping the Store and Adding DevTools

branch: 04-middleware

First and foremost let’s focus on the issue(s) we need to debug, and start by adding the Redux DevTools to our store. We will start by creating a DevTools Container Component, and we’ll add this in a file named DevTools.js in the App/containers folder.

 import React from 'react'

import { createDevTools } from 'redux-devtools'
import { compose } from 'redux'

import LogMonitor from 'redux-devtools-log-monitor'
import DockMonitor from 'redux-devtools-dock-monitor'

export default createDevTools(
  <DockMonitor toggleVisibilityKey="h"
               changePositionKey="ctrl-q"
               changeMonitorKey="ctrl-m"
               defaultIsVisible={true}
               defaultPosition="right">
    <LogMonitor />
  </DockMonitor>
);

This component will be used to intercept data passing to and from our store, allowing us to debug our redux code, and make sure everything is working as intended. As you can see there are a few different hotkeys defined in our component, and these can be quite helpful in the future. In the meantime I’ll keep these set to their defaults, but I would suggest reading over the docs at some point to learn more about what you can do, and add to the devtools.

Moving on, we need to connect our new container component to the store, and this will start in our index.js file:

 import style from './style/style.scss'
import React from 'react'
import { render } from 'react-dom'
import configureStore from './store/ConfigureStore'
import { Provider } from 'react-redux'
import App from './components/App'
import ErrorBoundary from './components/ErrorBoundary'
import DevTools from './containers/DevTools'

const store = configureStore()

render(
  <Provider store={store}>
    <ErrorBoundary>
      <App />
      <DevTools />
    </ErrorBoundary>
    </Provider>,
  document.getElementById('app')
);

Now that it’s watching our store, we need to also allow it to see our actions, and we can do this by connecting it in the configureStore where the component will act as middleware for the redux store.

 import { createStore, compose } from 'redux'
import DevTools from '../containers/DevTools'

import rootReducer from '../reducers/rootReducer'

const configureStore = () => {

  const store = createStore(
    rootReducer,
    compose(
      DevTools.instrument()
    )
  );

  return store
}

export default configureStore

So now that we have the DevTools setup, why don’t we debug our current code, and find out why our counter isn’t working? So fire up the app, make sure our dev tools are working, and we can figure out what’s going on!

react redux dev tools

In the root of our project folder run the following command to start webpack, and then go to http://localhost:3000 in a browser. When it loads up, we should see something like this:

Finding and Fixing Bugs

To start why don’t we focus on why the title and total are not showing on the page. From the dev tools we can see the state is actually there, so we should be golden, but we’re not. Hmmm…

I can only assume that you are using Chrome, and already have React Developer Tools installed. If not, I would suggest doing exactly that. By using the React dev tools we can target the Title’s H1 tag, and look to see what props it has, which is none, so our issue is that the props are not actually making it to the view. While I won’t cover it here, we could always take this a step further, and add the React PropTypes to check these props (which are installed already).

 
App.propTypes = {
  counter: PropTypes.shape({
    title: PropTypes.string.isRequired,
    total: PropTypes.number.isRequired
  })
}

The thing is we don’t have to go that far. Take a moment to look at the state hierarchy again, and then the state we are calling the in the view. In the devtools we see that the state is state => counter => title, and this is because of the counter namespace we added in the redux setup. So instead of accessing state, we need to access state.counter.  We could do this by using this.props.state.counter.title for our title, but that means we need to do this for all of the others as well. Instead of adding more repetition to the code, we need to instead change our mapStateToProps setup in the App.js:

 function mapStateToProps(state) {
  return {
    state: state.counter
  }
}
Redux Counter

Now we don’t have to change anything else, and we should be able to see our state in the view. With the view fixed, we now need to test the counter. Whoa, now when we press either counter button, we lose our title. Now what??

Looking at the devtools once again, we see something odd. When we call COUNTER_UP or COUNTER_DOWN, the title is removed from the state. So what’s that all about? Well, let’s think about where our state is altered, and the answer is the reducer. So on to our CounterReducer file!

When dealing with changing Redux data, there are two things we want to remember:

  1. we never want to mutate the state, we want to return a new object
  2. when returning a new object, we want to return the entire state for that namespace

In our reducer we are not accomplishing #2, because we are only returning the total. Basically we want to make sure to return the total as well when returning our new object, but what happens when we have lots of properties in an object, do we write out each and every one? Well, we could, but no way are we going to put that sort of responsibility on ourselves. Instead we can make use of the JavaScript ES7 spread-operator instead.

 import * as types from '../actions/ActionTypes'
import initialState from './initialState';

export default function counterReducer(state = initialState, action) {
  switch(action.type) {
    case types.COUNTER_UP:
      return {
        ...state, total: state.total + 1
      }
    case types.COUNTER_DOWN:
      return {
        ...state, total: state.total - 1
      }
    default:
      return state;
  }
}

Pretty neat, huh? What the spread operator does is spread all of the missing properties into the object, so we can change what needs to be changed, but pass everything else with that one call.

Redux Time Travel

If you have ever heard of the words “Time Travel” used in conjunction with Redux, you are about to see where this immutable state shines when it comes to debugging.

Redux Time Travel

With the title still removed from state, starting at the bottom of the action list click on the COUNTER_UP and COUNTER_DOWN actions in the devtools view, and you will see Redux work backwards in time restoring the previous state. Clicking the Reset button at the top will completely reset the state, clicking Commit will set a new reset point in your state, clicking Revert will reset the state without resetting the Commit, and lastly the Sweep button will clean up and remove any actions in your current state which have been reversed.

Knowing how to use these will become nothing short of invaluable, especially when tracking down bugs between state changes. You can add to the devtools to make this ability more robust, but the default features are the “bread n’ butter” of redux debugging.

Middleware

Before we finish up for the day, let’s cover a topic I briefly mentioned earlier, and that is Redux Middleware. Middleware is by far the most underrated feature in Redux, but one of the most powerful features in many cases.

We will add our middleware directly to our store. By doing this the middleware can intercept actions and call other actions to change state based on what is being called. One of the most common features added as middleware is the ability to persist state by intercepting actions and saving the state to localStorage after the action is run.

In Part 3 we will address Asynchronous actions, but before we can incorporate these, we need to add the thunk middleware to make it a reality. So to take care of this we need to alter our configureStore file again, and add thunk by adding it to store.

import { createStore, applyMiddleware, compose } from 'redux'
import DevTools from '../containers/DevTools'
import thunkMiddleware from 'redux-thunk'

import rootReducer from '../reducers/rootReducer'

const configureStore = () => {

  const store = createStore(
    rootReducer,
    compose(
      applyMiddleware(
        thunkMiddleware
      ),
      DevTools.instrument()
    )
  );

  return store
}

export default configureStore

NOTE: To make sure the DevTools do not break always make sure it is always at the last entry in the compose.

Keep in mind this is not the only way to incorporate asynchronous actions, and there are other libraries that add more functionality, like my personal favorite Redux Sagas which allow you to use JavaScript generators as actions, Redux Observables that allow for RxJS observables, and even some code we can add to also allow promises. So many choices!

Overall we covered all of the basics we need to know on how redux works, and how we can use it in it’s simplest form. From this point on it’s all about making redux more powerful, refining our code, then optimizing it for larger apps, and use-cases. So that’s it for this section, and I will see you in the next part!

Next:  The Beginner’s Guide to Redux: Part 3 – Refactor