Understanding The Redux State Flow

A General Purpose React-Redux Boilerplate (Part I)

A General Purpose React-Redux Boilerplate Series

Part I: Understanding The Redux State Flow
Part II: The Mechanics of a React Component
Part III: Commonly Used React Components & Tricks
Github: https://github.com/kangzeroo/Kangzeroos-ES6-React-Redux-Boilerplate

If you’ve been keeping up with the latest trends in web development, you’ll know that ES6 (ECMAScript 2015) is the newest official version of Javascript being adopted worldwide. It’s a big upgrade, and certainly not one that should be overlooked. Another big movement is React, developed by Facebook and an open source prodigy. In a gist, what’s so special about React is its component-based approach to structuring apps, powered by the React virtual DOM that efficiently re-renders UI. Redux is the data sharing model used throughout your app. Combining the two, developers can create powerful web apps that dynamically respond to changes. That’s what users expect nowadays, and as good developers, we must respond to those market demands.

This tutorial series is intended for web developers who have heard about React but have little to no experience using it. There are a lot of articles on the Internet about the benefits of React-Redux, so I’m not going to re-state what has already been said. If you’re reading this article, then I assume you’re already convinced and ready to get your hands dirty. Part I of this tutorial series will walk-through the Redux flow of this app. By the end of this series you will have the confidence to create and a general purpose boilerplate to help you jumpstart your own projects. So let’s get started!

First, clone the Github repo at https://github.com/kangzeroo/Kangzeroos-ES6-React-Redux-Boilerplate.

$ git clone https://github.com/kangzeroo/Kangzeroos-ES6-React-Redux-Boilerplate.git

Then enter the cloned folder and install the dependencies.

$ cd Kangzeroos-ES6-React-Redux-Boilerplate
$ cd App
$ npm install
$ npm run start

This boilerplate uses a few libraries for basic extended functionality. We use babel to convert our ES6 code into ES5 in order to support older browser versions that our end-users may be using. URL routing is handled by react-router. In order to programmatically style our app, we use Radium to write CSS in Javascript! This gives us great fine-tuning ability as the styles we write are contained within our components, and we can dynamically generate CSS based on app logic. On the Redux side of things, we are using Redux-Thunk to be able to work with async, functions, promises and other advanced features that are normally not used in Redux. Lastly, React-Logger is a powerful debugging tool to see Redux state changes from the web browser console.

After running $ npm install and $ npm start, go to your web browser and visit http://localhost:8080. You should see the following screen:

Now open up the code in your favorite text editor. The boilerplate is heavily commented, so you could understand it eventually just by reading the comments. But for the sake of structure, direction, and time savings, let’s go through it together.

First, a quick run-down of how Redux works. Check out this nice diagram, curtesy of CSS-tricks.com

Each one of these nodes represents a React component. Components could be a profile header, a tweet newsfeed, a form… you get the point. These components are “presentational”, aka “dumb” components. That means the code in the component deals only with presenting what the user sees. The data behind the components originate from the Redux Store, which can be thought of as a giant global variable. The data inside here is called the state of an app. When a component wants to change some data, it will dispatch (explained later in this article) an action to the Redux Store, which determines the next state of the app based on logic we define. When the Redux Store is updated, any components that depend on the state will be updated to reflect the latest changes. What is important to take away from this is that components never talk directly to eachother. They all communicate with the Redux Store, which is the single grain of truth across the entire app. This leads to simpler state management, and thus allowing us to create apps of greater complexity.

So let’s start off with the Redux Store. Go to ./App/src/reducers/contentReducer.js and take a look at const INITIAL_STATE. This variable represents our initial state, and we have defined only 1 property called myContent, which is an array.

import { ADD_CONTENT } from '../actions/action_types'
const INITIAL_STATE = {
myContent: []
}
export default function(state = INITIAL_STATE, action){
switch(action.type){
case ADD_CONTENT:
return {
...state,
myContent: state.myContent.concat([action.payload])
}
}
return state
}

Let’s see how our components connect with the Redux state. Go to ./App/src/components/home.js and scroll down to find the following code:

function mapStateToProps(state){
return {
myContent: state.content.myContent
}
}

What we are doing here is mapping the Redux state to the Home component. We want the myContent variable, so we get it from state.content.myContent. Pretty straightforward, but what is state.content? To understand this, we must go to ./App/src/reducers/index.js. This is what we will find:

import { combineReducers } from 'redux';
import contentReducer from './contentReducer'
import sideMenuReducer from './sideMenuReducer'
const rootReducer = combineReducers({
content: contentReducer,
sideMenu: sideMenuReducer
})
export default rootReducer

Since our app can get fairly complex, we have multiple reducers to handle different sub-states. The content sub-state is defined by contentReducer.js and handles anything to do with the content of this boilerplate. The sideMenu sub-state handles anything to do with the sideMenu functionality of this boilerplate. The rootReducer is our full Redux State, and it is simply a Javascript object. So it makes sense that if we want to access the myContent variable inside contentReducer, we must use state.content.myContent to go deeper down the Redux State object.

At this point you may be wondering, what is a reducer? Previously we said that the state of our app is calculated based on logic we define. Reducers are that logic! Take a look at the rest of ./App/src/reducers/contentReducer.js:

export default function(state = INITIAL_STATE, action){
switch(action.type){
case ADD_CONTENT:
return {
...state,
myContent: state.myContent.concat([action.payload])
}
}
return state
}

When we say that a component is dispatching an action to the Redux Store, it is sending an action object that looks like this:

{
type: ADD_CONTENT,
payload: value
}

Our contentReducer.js is taking in these incoming actions and checking their type. In the case that type == ADD_CONTENT, we want to return the exact same state but with myContent slightly changed. According to our contentReducer logic, myContent will have the payload of the action appended to the array. After this state update is complete, we return the newly changed state object.

But recall that ./App/src/components/home.js was watching the state.content.myContent variable. Because home.js cares about (aka is subscribing to) myContent, then whenever myContent changes, so will the home.js UI. That’s great because your UI will always be showing the correct latest information.

If home.js wants to the ability to change myContent, it will have to send an action. Inside ./App/src/components/home.js, find this line of code that executes when we press the submit button:

submitContent(){
convertToLowerCase(this.state.textValue)
.then((data)=>{
this.props.addValueToContent(data)
this.setState({
textValue: ""
})
})
}

What we care about here is this.props.addValueToContent(data). This is an action creator, defined in ./App/src/actions/contentActions.js. If we look at contentActions.js we see this:

export function addValueToContent(value){
return function(dispatch){
dispatch({
type: ADD_CONTENT,
payload: value
})
}
}

See the connection? In our home.js component we are firing off this action, and when we do, we are passing in a value. The contentReducer catches the type: ADD_CONTENT and payload: value to determine the next state. In this code, we tell Redux to concatenate the payload to the myContent array… and that’s it! Let me save you some scrolling and re-display the code inside ./App/src/reducers/contentReducer.js:

const INITIAL_STATE = {
myContent: []
}
export default function(state = INITIAL_STATE, action){
switch(action.type){
case ADD_CONTENT:
return {
...state,
myContent: state.myContent.concat([action.payload])
}
}
return state
}

myContent inside the Redux State has changed. Let’s see this in action from our UI. Try typing something into the app and click “Add to Redux State”:

You should see the Redux State update, and the UI reflect these changes. Look inside your browser’s Javascript console and we see Redux-Logger at work:

We can see the previous Redux state, the action that was sent, and the resulting Redux state. Wow! We just went through the entire Redux state flow and its results.

Perhaps this is walkthrough was enough to get you comfortable with this boilerplate. If so, great! But for everyone else, check out Part II to continue our exploration of this React-Redux Boilerplate where we explain the specifics of a React component.

These methods were partially used in the deployment of renthero.ca