Getting Started with Redux — SitePoint

For a high-quality, in-depth introduction to React, you can’t go past Canadian full-stack developer Wes Bos. Try his course here, and use the code SITEPOINT to get 25% off and to help support SitePoint.

A typical web application is usually composed of several UI components that share data. Often, multiple components are tasked with the responsibility of displaying different properties of the same object. This object represents state which can change at any time. Keeping state consistent among multiple components can be a nightmare, especially if there are multiple channels being used to update the same object.

Take, for example, a site with a shopping cart. At the top we have a UI component showing the number of items in the cart. We could also have another UI component that displays the total cost of items in the cart. If a user clicks the Add to Cart button, both of these components should update immediately with the correct figures. If the user decides to remove an item from the cart, change quantity, add a protection plan, use a coupon or change shipping location, then the relevant UI components should update to display the correct information. As you can see, a simple shopping cart can quickly become difficult to keep in sync as the scope of its features grows.

In this guide, I’ll introduce you to a framework known as Redux, which can help you build complex projects in way that’s easy to scale and maintain. To make learning easier, we’ll use a simplified shopping cart project to learn how Redux works. You’ll need to be at least familiar with the React library, as you’ll later need to integrate it with Redux.

Prerequisites

Before we get started, make sure you’re familiar with the following topics:

What is Redux

Redux is a popular JavaScript framework that provides a predictable state container for applications. Redux is based on a simplified version of Flux, a framework developed by Facebook. Unlike standard MVC frameworks, where data can flow between UI components and storage in both directions, Redux strictly allows data to flow in one direction only. See the below illustration:

Figure 1: Redux Flow Chart

In Redux, all data — i.e. state — is held in a container known as the store. There can only be one of these within an application. The store is essentially a state tree where states for all objects are kept. Any UI component can access the state of a particular object directly from the store. To change a state from a local or remote component, an action needs to be dispatched. Dispatch in this context means sending actionable information to the store. When a store receives an action, it delegates it to the relevant reducer. A reducer is simply a pure function that looks at the previous state, performs an action and returns a new state. To see all this in action, we need to start coding.

Understand Immutability First

Before we start, I need you to first understand what immutability means in JavaScript. According to the Oxford English Dictionary, immutability means being unchangeable. In programming, we write code that changes the values of variables all the time. This is referred to as mutability. The way we do this can often cause unexpected bugs in our projects. If your code only deals with primitive data types (numbers, strings, booleans), then you don’t need to worry. However, if you’re working with Arrays and Objects, performing mutable operations on them can create unexpected bugs. To demonstrate this, open your terminal and launch the Node interactive shell:

node

Next, let’s create an array, then later assign it to another variable:

As you can see, updating array b caused array a to change as well. This happens because Objects and Arrays are known referential data types — meaning that such data types don’t actually hold values themselves, but are pointers to a memory location where the values are stored. By assigning a to b, we merely created a second pointer that references the same location. To fix this, we need to copy the referenced values to a new location. In JavaScript, there are three different ways of achieving this:

In the above code example, array b can now be modified without affecting array a. We’ve used Object.assign() to create a new copy of values that variable b will now point to. We can also use the rest operator(...) to perform an immutable operation like this:

The rest operator works with object literals too! I won’t go deep into this subject, but here are some additional ES6 functions that we’ll use to perform immutable operations:

In case the documentation I’ve linked isn’t useful, don’t worry, as you’ll see how they’re used in practice. Let’s start coding!

Setting up Redux

The fastest way to set up a Redux development environment is to use the create-react-app tool. Before we begin, make sure you’ve installed and updated nodejs, npm and yarn. Let’s set up a Redux project by generating a redux-shopping-cart project and installing the Redux package:

1st statement. We import a createStore() function from the Redux package.

2nd statement. We create an empty function known as a reducer. The first argument, state, is current data held in the store. The second argument, action, is a container for:

type — a simple string constant e.g. ADD, UPDATE, DELETE etc.

payload — data for updating state

3rd statement. We create a Redux store, which can only be constructed using a reducer as a parameter. The data kept in the Redux store can be accessed directly, but can only be updated via the supplied reducer.

You may have noticed I mentioned current data as if it already exists. Currently, our state is undefined or null. To remedy this, just assign a default value to state like this to make it an empty array:

const reducer = function(state=[], action) { return state; }

Now, let’s get practical. The reducer we created is generic. Its name doesn’t describe what it’s for. Then there’s the issue of how we work with multiple reducers. The answer is to use a combineReducers function that’s supplied by the Redux package. Update your code as follows:

In the code above, we’ve renamed the generic reducer to cartReducer. There’s also a new empty reducer named productsReducer that I’ve created just to show you how to combine multiple reducers within a single store using the combineReducers function.

Next, we’ll look at how we can define some test data for our reducers. Update the code as follows:

Just to confirm that the store has some initial data, we use store.getState() to print out the current state in the console. You can run the dev server by executing npm start or yarn start in the console. Then press Ctrl+Shift+I to open the inspector tab in Chrome in order to view the console tab.

Figure 2: Redux Initial State

Currently, our cartReducer does nothing, yet it’s supposed to manage the state of our shopping cart items within the Redux store. We need to define actions for adding, updating and deleting shopping cart items. Let’s start by defining logic for a ADD_TO_CART action:

Take your time to analyze and understand the code. A reducer is expected to handle different action types, hence the need for a SWITCH statement. When an action of type ADD_TO_CART is dispatched anywhere in the application, the code defined here will handle it. As you can see, we’re using the information provided in action.payload to combine to an existing state in order to create a new state.

Next, we’ll define an action, which is needed as a parameter for store.dispatch(). Actions are simply JavaScript objects that must have type and an optional payload. Let’s go ahead and define one right after the cartReducer function:

Here, we’ve defined a function that returns a plain JavaScript object. Nothing fancy. Before we dispatch, let’s add some code that will allow us to listen to store event changes. Place this code right after the console.log() statement:

After you’ve saved your code, Chrome should automatically refresh. Check the console tab to confirm that the new items have been added:

Figure 3: Redux Actions Dispatched

The index.js file has quickly grown large. This is not how Redux code is written. I’ve only done this to show you how simple Redux is. Let’s look at how a Redux project should be organized. First, create the following folders and files within the src folder, as illustrated below:

After you’ve finished updating the code, the application should run as before now that it’s better organized. Let’s now look at how we can update and delete items from the shopping cart. Open cart-reducer.js and update the code as follows:

Your browser should automatically refresh once you’ve saved all the changes. Check the console tab to confirm the results:

Figure 4: Redux Update and Delete Actions

As confirmed, the quantity for 1kg of flour is updated from 2 to 5, while the the 500gm of coffee gets deleted from cart.

Now, if we’ve made a mistake in our code, how do we debug a Redux project?

Redux comes with a lot of third-party debugging tools we can use to analyze code behavior and fix bugs. Probably the most popular one is the time-travelling tool, otherwise known as redux-devtools-extension. Setting it up is a 3-step process. First, go to your Chrome browser and install the Redux Devtools extension.

Figure 5: Redux DevTools Chrome Extensions

Next, go to your terminal where your Redux application is running and press Ctrl+C to stop the development server. Next, use npm or yarn to install the redux-devtools-extension package. Personally, I prefer Yarn, since there’s a yarn.lock file that I’d like to keep updated.

yarn add redux-devtools-extension

Once installation is complete, you can start the development server as we implement the final step of implementing the tool. Open store.js and replace the existing code as follows:

Feel free to update src/index.js and remove all code related with logging to the console and subscribing to the store. This is no longer needed. Now, go back to Chrome and open the Redux DevTools panel by right-clicking the tool’s icon:

Figure 6: Redux DevTools Menu

In my case, I’ve selected to To Bottom option. Feel free to try out other options.

Figure 7: Redux DevTools Panel

As you can see, the Redux Devtool is quite amazing. You can toggle between action, state and diff methods. Select actions on the left panel and observe how the state tree changes. You can also use the slider to play back the sequence of actions. You can even dispatch directly from the tool! Do check out the documentation to learn more on how you can further customize the tool to your needs.

Integration with React

At the beginning of this tutorial, I mentioned Redux really pairs well with React. Well, you only need a few steps to setup the integration. Firstly, stop the development server, as we’ll need to install the react-redux package, the official Redux bindings for React:

yarn add react-redux

Next, update index.js to include some React code. We’ll also use the Provider class to wrap the React application within the Redux container:

Just like that, we’ve completed the first part of the integration. You can now start the server to see the result. The second part involves linking React’s components with the Redux store and actions using a couple of functions provided by the react-redux package that we just installed. In addition, you’ll need to set up an API using Express or a framework like Feathers. The API will provide our application with access to a database service.

In Redux, we’ll also need to install further packages such as axios to perform API requests via Redux actions. Our React components state will then be handled by Redux, making sure that all components are in sync with the database API. To learn more on how to accomplish all this, do take a look at my other tutorial, “Build a CRUD App Using React, Redux and FeathersJS”.

Recommended Courses

A step-by-step training course to get you building real world React.js + Firebase apps and website components in a couple of afternoons. Use coupon code 'SITEPOINT' at checkout to get 25% off.

I hope this guide has given you a useful introduction to Redux. There’s still quite a bit more for you to learn, though. For example, you need to learn how to deal with async actions, authentication, logging, handling forms and so on. Now that you know what Redux is all about, you’ll find it easier to try out other similar frameworks, such as Flux, Alt.js or Mobx. If you feel Redux is right for you, I highly recommend the following tutorials that will help you gain even more experience in Redux:

Meet the author

I write clean, readable and modular code. I love learning new technologies that bring efficiencies and increased productivity to my workflow. Currently am into React & Javascript stuff.