June 30, 2017

Becoming Familiar with Redux

by Thomas Greco

becoming-familiar-with-redux

Redux has become an incredibly powerful tool for managing state in JavaScript applications. Redux is usually partnered with ReactJS, but it can be integrated wherever one wishes to use it. There have also been a number of libraries created for Redux integration, such as ngRx for Angular. Regardless of how Redux is being implemented, the core concepts behind it will always be the same. Today, we’re going to get a taste of how Redux works and take a look at the core concepts of the framework. By the end, we’ll have a low-level counter application.

Core concepts of Redux

“Immutable State Tree”

As JavaScript use continues to grow, so does the complexity of the JavaScript applications being built. In turn, there have been many libraries created to handle the state (or data) of an application, as this is often the most difficult part of development. At the heart of Redux is the immutable state tree, known as the store, which is just an object representation of all of an application’s data. A redux store is often referred to as the single-source of truth. It allows for great debuggability as users will be informed immediately whether the state is updating correctly, and it even allows users to “time-travel” to previous states to see what is going on.

“State is Read-only”

An application’s store is read-only. It can only be changed by dispatching actions objects.

store.dispatch({  
   Type: ‘INCREMENT’
});

Soon, we will see how to use action objects in better detail. Right now, however, it’s important to understand that enforcing a read-only state eliminates the need to worry about outside factors causing a mutation in application data, and that is just wonderful.

“Changes are made with pure functions”

To successfully update an application state, we must use a redux reducer, which will be used with JavaScript’s reduce function, to create the next application state. The reducer function is a pure function.

A pure function is a function that will always map the same input to the same output. They are essential to building immutable JavaScript applications, as they allow developers to look at the code and easily determine the result of a function. All a developer needs to do is take a look at the Redux-style reducer function, and the action object that is being dispatch. For those interested in truly learning more about reduce, and why it’s one of the most powerful functions JavaScript has to offer, check out this link.

Now that we know the core concepts, let’s dive into a sample application built using create-react-app. The code for this project can be found at this GitHub repository.

The first function we want to take a look at is our reducer function. Redux users will spend a lot of time using reducer functions, so it’s important to understand how they work.

// index.js
const reducer = (state = 0, action) => {  
  switch(action.type) {
   case ‘INCREMENT’:
    return state + 1;
   case ‘DECREMENT’:
    return state - 1;
  default: return state;
}

The reducer function takes two arguments: state and action. The state argument is the state of our application when an action was triggered. Because ES6 is so awesome, we can set a default value for our state. In our case, the state currently is 0. In addition to our previous state, the reducer also takes the action objects mentioned earlier.
Actions objects must have a type property but may contain others properties such as payload. The type property is essential as it is what our reducer uses to determine what update it should make to the state. To accomplish this, reducer functions include switch... case statements when updating state. Taking a look at this statement, we will see that it will switch on an action’s type property. Specifically, it will add 1 to the counter when an action with a type of INCREMENT is passed in, while DECREMENT will subtract the counter by 1. In both instances, the result will be a new state object.

CSS Tricks Redux Diagram

An application’s store can only be updated by dispatching action objects. To perform this, we need to use the store.dispatch method. Our example has the following code for a Counter component.

// index.js
const Counter = ({value}) => {  
 return (
  <div>
   <h3>Counter</h3>
   <button onClick={() => store.dispatch({ type: 'INCREMENT'})}>+</button>
   {value}
  </div>
 )
}

Inside the return statement for this component, we see the store.dispatch method being passed into react’s onClick handler. Each time a button is clicked, our store will be updated accordingly. Following this, we see the expression, {value}, that will evaluate and render our application’s state, which in this case is the number of times the button has been clicked.

Final Touches

In order for all of this to work, we need to use Redux’s createStore() method. createStore needs to be imported from the Redux library, and it takes a reducer function. Our store declaration should look like this.

import React from 'react'  
import ReactDOM from 'react-dom'  
import { createStore } from 'redux'  
const reducer = (state = 0, action) => {  
 switch (action.type) {
   case 'INCREMENT': return state + 1;
   case 'DECREMENT': return state - 1;
   default: return state;
 }
}

const store = createStore(reducer);

/** counter component **/
const Counter = ({value}) => {  
  return (
    <div>
      <h3>Counter</h3>
      <button onClick={() => store.dispatch({ type: 'INCREMENT'})}>+</button>
      {value}
    </div>
  )
}

Now that our store has been created, we need to use two more store methods: getState and subscribe. We’re going to use getState() to populate the value property that is passed into the Counter component by simply passing it into our <Counter> tag.

// index.js
const render = () => {  
 ReactDOM.render(
   <Counter value={store.getState()} />,
   document.getElementById('root')
 )
}

This will give our component access to the state, but we still need to subscribe to our store. If we don’t our application won’t know when any state changes happen. In this example, we’re going to subscribe to our render const like so.

//index.js
store.subscribe(render);  

It is important to note that there are libraries like react-redux which provide users with a number of tools that make react-redux integration a breeze. Nonetheless, we have successfully created our first redux counter app! The final code sample for our index.js file should look like the following:

//index.js
import React from 'react'  
import ReactDOM from 'react-dom'  
import { createStore } from 'redux'  
const reducer = (state = 0, action) => {  
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
}
const store = createStore(reducer);  
const Counter = ({value}) => {  
  return (
    <div>
     <h3>Counter</h3>
     <button onClick={() => store.dispatch({ type: 'INCREMENT'})}>+</button>
     {value}
    </div>
  )
}
const render = () => {  
  ReactDOM.render(
   <Counter value={store.getState()} />,
    document.getElementById('root')
  )
}
store.subscribe(render);  
render();  

You made it!

Congratulations! You successfully worked your way through a reactJS application with Redux architecture. Hopefully by now, you have gained a bit of knowledge regarding how Redux works. In the meantime, I encourage those interested in Redux to check out the following links as they are great sources of information.