Redux Quick Start Guide

By James Lee , Tao Wei , Suresh Kumar Mukhiya
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies

About this book

Starting with a detailed overview of Redux, we will follow the test-driven development (TDD) approach to develop single-page applications. We will set up JEST for testing and use JEST to test React, Redux, Redux-Sage, Reducers, and other components. We will then add important middleware and set up immutableJS in our application. We will use common data structures such as Map, List, Set, and OrderedList from the immutableJS framework. We will then add user interfaces using ReactJS, Redux-Form, and Ant Design.

We will explore the use of react-router-dom and its functions. We will create a list of routes that we will need in order to create our application, and explore routing on the server site and create the required routes for our application. We will then debug our application and integrate Redux Dev tools.

We will then set up our API server and create the API required for our application. We will dive into a modern approach to structuring our server site components in terms of Model, Controller, Helper functions, and utilities functions. We will explore the use of NodeJS with Express to build the REST API components. Finally, we will venture into the possibilities of extending the application for further research, including deployment and optimization.

Publication date:
February 2019
Publisher
Packt
Pages
204
ISBN
9781789610086

 

Chapter 1. Understanding Redux

With the great prevalence of web applications and companies transforming from traditional desktop-based systems to web-based systems, there are now a multitude of opportunities on the World Wide Web. There are various programming languages for server-side scripting (SSR), client-side scripting, presentation logic (HTML and CSS), and query languages. JavaScript is one of the most popular languages on the web, and it encompasses several frameworks that assist in its development, compilation, and production. React is one of the most popular JavaScript frameworks, and it is developed and distributed by Facebook. While React helps in building highly sophisticated, interactive user interfaces, Redux, on the other hand, is getting very popular in the frontend community for state management. In this chapter, we will get you familiarized with React, the concept of functional programming, the major components of Redux, and how to get started with it.

We will discuss the following topics in this chapter:

  • The need for Redux 
  • The concept of functional programming
  • The components of Redux
  • Getting started with Redux
  • Setting up the project
 

The need for Redux


The amalgamation of React and Redux is trending over the internet, but this popularity should not be a reason for using Redux in your application. Instead, you should be asking why you need Redux. What problems does it solve? A lot of technical books and blogs claim that Redux facilitates state management. That statement, in itself, is very vague. This is even vaguer a claim given that React also has state management. So, why should we use Redux in our applications?

React has a unidirectional data flow. The data is passed to a lower component by using props. For example, consider a simple state machine, as shown in the following screenshot:

The main component, App, holds the state of the machine and the props. The state status is passed down as the props, as follows:

In order to change the data up to the tree, a callback function must be passed as the props to any component that changes the state:

This is a normal scenario in any React application. When you keep building the application, more and more components are aggregated. Your application will react to the state; you will have layers of components, and the top layer will pass props to the child components. To understand this scenario, let's look at the example of Pinterest:

To achieve the layout shown in the preceding screenshot, we will require several components, and each component will need to pass props and states to its child components. A mocked-up version for different components would look something like the following snippet:

<App state={user: {}}>
 <Navbar user = {user}>
   <Logo />
   <SearchBar />
   <Menu> ... </Menu>
 </Navbar>
 <Content user={user}>
  <TitleBar user={user} />
  <Avatar user={user} />
  <Board data={data} />
  ....
 </Content>
 <Footer />
</App>

In the preceding snippet, it is obvious that some props and states are used multiple times. For example, the Avatar component, the TitleBar component, and the Navbar component require user information. In order to deliver user information, each of the parent components must pass the props to their child components. It is possible to achieve this by passing the props; however, it will be cumbersome and painful if we have a bunch of components working together. By now it must be obvious that, when we work with React, we are dealing with a lot of components interacting with each other.

Instead of an intermediate component accepting and passing along the props, it would be nice if the component did not need to know about the data. This is the problem that is solved by Redux. It provides direct access to the required dataset. 

It is very intimidating to start coding your application. However, you can avoid a lot of hassle and debugging time if you can model your application. There are several tools that are available to model your application. If your model looks like the one in the preceding example, you can consider using Redux. If you feel the need to cache data between views and remember the data for the next layer, Redux is the best option. Finally, if you know that your application is large and your web application will deal with a large set of data that will fluctuate over time, Redux is a good option; it will help you to build an abstraction between the physical layer and the data layer.

Frequently asked questions

The following is a list of frequently asked questions about Redux:

  • Can I use Redux without React?Yes; Redux is an elegant library for state management. It can be used with any other library, including Vanilla JS, Angular, Vue JS, JQuery, Ember, Aurelia, and others.
  • Do I need Redux to build React applications? No; Redux facilitates managing the data layer in React applications. It really depends on the type of application that you are building. The amalgamation of React with Redux is very popular on the web, but you should really think about whether you need Redux. React already does state management, so using Redux for smaller applications will be overkill.
  • What do I need to use Redux? You will need ES6, ES5, or later versions.
 

Functional programming


A lot of blogs, books, online tutorials, videos, and courses found on the World Wide Web start with a common statement, saying that Redux was built on the top of functional programming. The statement is valid, which means that developers like us need to understand the concept of functional programming.

Let's point out some of the important characteristics of functional programming, as follows:

  1. Functions are first class objects
  2. Functions can be chained together
  3. Functions can be passed as arguments
  4. Functions, recursions, and an array can be used to control the flow
  5. We can use pure, higher-order, closure, and anonymous functions
  6. We can utilize several helper functions, including map, filter, and reduce

In functional programming, functions are considered first class citizens. This means that the language does support passing functions to other functions as arguments, and returning them as the values for other functions. Moreover, they can also be assigned to other variables, or stored in some data structure.

Assigning functions to variables

An example of calculating body mass index (BMI), provided the height (in meters) and weight (in kilograms), can be created via the following method. The function is stored in a variable named bmi and can be called whenever it is required:

const bmi = (weight, height) => weight / (height * height);

Adding functions to objects and arrays

A variable can be added to any object. Since a function is stored in a variable, it can also be added to objects, as follows:

const myCalculator = {
    bmi: (weight, height) => weight / (height * height)
};

Similarly, we can add it to an array, as follows:

const myCalculator = [
 Bmi => (weight, height) => weight / (height * height)
];

Functions as arguments

Functions can be used as arguments for other functions. Let's use the preceding bmi function to check whether a person has an obesity issue. According to the BMI scale, someone with a bmi between 30.0 and 54 is said to have obesity. We will pass a function as an argument, as follows:

const bmi = (weight, height) => weight / (height * height);
const hasObesity = (bmi) => bmi >= 30.0 && bmi <=54;
console.log(hasObesity(bmi(100, 2.2)));

Functions returned by functions

Another common scenario is when a function returns another function, as follows:

const bmi = (weight, height) => weight / (height * height);
const calculator = () => {
 return bmi;
};

Higher-order functions

Higher-order functions (HOF)  is the fanciest term you will be hearing when getting started with functional programming. Higher-order functions are functions that take functions as arguments or return functions. By now, we have already been consuming such functions. Remember Array.reduce(), Array.filter(), and Array.map()? These are all higher-order functions. In the Redux library, we are consuming some of the HOF, too (such as connect()).

Pure functions

The most common definition of a pure function is a function that does not have side effects. This is to say that the returned value of a pure function is not affected, influenced, or changed by anything other than its input parameters. Provided the same input, the pure function always generates the same output. An example is as follows:

const sum = (a, b) => a + b;

This is an example of a pure function. Assuming that you call the function sum(6,9), the result is always 15, irrespective of the number of times that you run the function. You can be confident, if you are calling a pure function with the same input, that you are always going to get the same output, which means that the output is predictable. An example of an impure function is the following square function. In addition to returning the square of the number, the function might be updating the number in a database:

function square(number) {
 updateNumberInDB(number);
 return number * number;
}

Compositions

A composition is a very important concept of functional programming; it is how we create a higher-order function by consuming and combining simpler functions.

Let's just use the sum function that we defined previously. We can split the sum function into the following composition:

const sum = a => b => a + b
sum(2)(5)

Any function can be transformed into a composable function via the concept of currying. Explaining these fundamental functional concepts is beyond the scope of this book, and we suggest that you get familiar with functional terms as much as possible, in order to get a full understanding of Redux and React. We will consume a composition function from Redux, called compose, in upcoming chapters.

 

Fundamental principles of Redux


Redux makes it possible to store all statuses in an application in a single place, which is called a store. A store is the intermediary to all of the changes of the status of the app. Using Redux, a component cannot communicate directly with another component; instead, the changes always go through a single source, which is an action. Redux can be described simply by three fundamental principles. Those three main principles, which will be briefly explained in this chapter, are summarized as follows:

  • A single source of truth
  • The read-only nature of the state
  • The reducer principle

Single source of truth

The whole application state is stored in a single object, called the state tree. This makes it easier to create modern applications, as the server state can easily be serialized and hydrated to client apps.

An example for an online medical store app is as follows:

Read-only nature of the state

Emitting an action is the only way to change the state of a Redux app. Views cannot directly write to the state tree. In Redux, every intent needs to dispatch actions, which tell the reducers (a function) to transform the state. Mutating the state is also not recommended; hence, every time, the reducers write the existing state object with the new version:

The reducer principle – changes are made with pure functions

The transformation logic of the state tree is specified with the use of pure functions, called reducers. Reducers are special functions that take the current state and action to return a new state, without mutating the state.

 

The Redux ecosystem


When we talk about Redux, we usually include other libraries that work together in harmony. Here, we will discuss some of the important libraries that work well together, as follows:

  • react-redux: This allows us to communicate in both directions, between React and Redux (https://github.com/reactjs/react-redux). It is a binding between React and Redux that allows us to create containers and listen to the store changes, reflecting that into a presentational component. We will explore more about container components (smart components) and presentational components (dumb components) in upcoming chapters.
  • redux-devtools: This is the official implementation of developer tools for Redux, and it allows for watching state changes, live to edit actions, time traveling, and more (https://github.com/gaearon/redux-devtools).
  • redux-promise: This is middleware for Redux, allowing you to dispatch JavaScript promises to the Redux store (https://github.com/acdlite/redux-promise).

An official overview of the Redux ecosystem can be found on the Redux website, at http://redux.js.org/docs/introduction/Ecosystem.html.

There is a community-maintained repository called awesome Redux. This repository contains resources, libraries, utilities, boilerplate code, and examples associated with Redux, and is located at https://github.com/xgrommx/awesome-redux.

 

Elements of Redux


To understand Redux, we need to understand its components. There are four main elements of Redux; let's discuss each of them, one by one.

Actions

Actions are simply JavaScript objects describing the changes in the state of the application. To be specific, they are payloads of information that transfer data from our application to the state. Does this not make sense to you? No problem. Let's look at an example use case. Suppose that we need to add a doctor's information to our hospital management system:

const ADD_NEW_DOCTOR_REQUEST = "ADD_NEW_DOCTOR_REQUEST"

It isn't rocket science, right? It's just a simple, constant ADD_DOCTOR_REQUEST. Now, let's create an object:

{
 type: ADD_NEW_DOCTOR_REQUEST,
 data: {
   name: ‘Dr. Yoshmi Mukhiya’,
   age: 22,
   department: ‘Mental Health’,
   telecom: ‘99999999’
 }
}

This is a simple, plain JavaScript object, and it is referred to as an action. An action must have the type property that defines the type of action to be performed. In this use case, the action is adding an action. The type is basically a string constant. In any web application, there are a multitude of actions required. So, the general (and most common) trend is to separate these actions into separate files and import them into the required place.

Now, let's assume that we need to delete a doctor's record from our app. We should be able to create an action object easily, as follows:

{
 type: 'DELETE_DOCTOR_REQUEST',
 identifier: 201,
}

Now, go ahead and create the actions for the following:

  1. Adding a user to the hospital management system
  2. Deleting a user from the hospital management system
  3. Updating a user

Action creators

JavaScript functions that take some arguments and return actions are action creators. Let's look at an action creator function for adding a new doctor to the application:

function addNewDoctor(data) {
 return {
   type: ADD_NEW_DOCTOR_REQUEST,
   data
 };
}

Now, you can think of a function that you might need for deleting a record, as follows:

function deleteDoctor(identifier) {
 return {
   type: "DELETE_DOCTOR_REQUEST",
   identifier
 };
}

Before we move on to reducers, let's make one more action creator for authentication. Generally, to authenticate, we use an email and password. So, in order to authenticate (or deauthenticate) we need to define actions. Please note that the actions that we define will be used in our project for a hospital management system. Our action for authentication could look something like the following:

export const authenticate = (credentials) => ({
 type: "AUTHENTICATE",
 payload: credentials
});
export const deauthenticate = () => ({
 type: "DEAUTHENTICATE"
});

Similarly, let's create action creators for registering a user. When we register a user, we are likely to have a request, a success, or a failure. Based on these three states, we can create the action creators, as follows:

export const onRegisterRequest = user => ({ type: REGISTER_REQUEST, user });

export const onRegisterSuccess = user => ({ type: REGISTER_SUCCESS, user });

export const onRegisterFailure = message => ({
  type: REGISTER_FAILURE,
  message,
});

Reducers

JavaScript functions that take actions and states as input and return the new states are reducers. Well, if this is confusing, try to keep in mind that the action only describes what happened, not how the application state transforms.

It is very important to understand the reducer function. Let's consider our hospital management system. Our application's state can look like the following:

{
 doctors: [
   {
     name: "John Doe",
     department: "Radiology",
     address: "Kathmandu, 4017, Nepal",
     telecom: "999-999-999"
   },
   {
     name: "Ola Nordmann",
     department: "General Physician",
     address: "Kong Oscarsgate 29, 5017, Bergen, Norway",
     telecom: "111-111-1111"
   }
 ];
}

When creating a reducer function, it is important that we remember the reducer principle: it must be a pure function. It should just take the action and return a new state, with no side effects, no mutations, and no API calls.

Let's consider another example of a content management system. In a normal CMS, we have posts and categories. So, our state at an instance could look like the following:

{
 posts: [
   { user: 'John Doe', category: 'Practitioner', text: 'This is the first post about Practitioner.' },
   { user: 'Ola Nordmann', category: 'Patients', text: 'This is the first post about Patients.' }
 ],
 filter: ‘Patients’
}

There's nothing complicated here, right? Now, let's start to write our reducer function for both use cases: our CMS use case and our hospital management system use case.

We will start by defining an initial state. Let's initiate our initial state by creating an empty object with an array of empty doctors:

const initialState = {
 doctors: []
};

In any database, there is a need for creating, updating, reading, and deleting resources. Similarly, in the hospital management system, we need to read a doctor's record, create a new record, update it, or delete it. Hence, we are likely to have multiple action objects defined, as we mentioned in the preceding section.

This introduces a requirement to handle reducer functions for each of the actions. We can create a single reducer function to handle a similar scenario, and make use of the switch case to handle multiple action types:

import {
 ADD_NEW_DOCTOR_REQUEST,
} from './actions'

function addDoctor(state = initialState, action) {
 switch (action.type) {
   case ADD_NEW_DOCTOR_REQUEST:
     return Object.assign({}, state, {
       doctors: [
         ...state.doctors,
         {
           name: action.name,
           age: action.age,
           department: action.department,
           telecom: action.telecom
         }
       ]
     });
   default:
     return state;
 }
}

In the preceding snippet, we have defined ADD_NEW_DOCTOR_REQUEST in the actions. We can check the action type for deleting the doctor's record. Go ahead and add a reducer use case for deleting a doctor.

Now, your task is to check the initial state of the CMS system and write reducer functions for CREATE_POST, EDIT_POST, and SET_FILTER. Once you have finished writing the reducer function, it should look something like the following:

import { CREATE_POST, EDIT_POST, SET_FILTER } from './actionTypes'

function postsReducer (state = [], action) {
 switch (action.type) {
   case CREATE_POST: {
     const { type, ...post } = action
     return [ ...state, post ]
   }

   case EDIT_POST: {
     const { type, id, ...newPost } = action
     return state.map((oldPost, index) =>
       action.id === index
         ? { ...oldPost, ...newPost }
         : oldPost
     )
   }

   default:
     return state
 }
}

Store  

The store stores all of the states of the application. Hence, it is sometimes referred to as the heart of the application. The most important point to note is that there is a single store in the entire application. To create a store, we can use the createStore function provided by Redux:

import { createStore } from 'redux'
import doctorsReducer from './reducers'
const store = createStore(doctorsReducer)

The methods for stores will be explained in the following subsections.

getState()

The getState() method gives you the current state of any application, which is equal to the last value returned by the application's reducer.

dispatch(action)

As the name suggests, dispatch(action) only dispatches the action. The main point to keep in mind is that this is the single way to modify the state.

subscribe(listeners)

The subscribe(listeners) method adds a change listener, which is called any time an action is dispatched, and some part of the state tree may potentially have changed.

replaceReducer(nextReducer)

The replaceReducer(nextReducer) method replaces the reducer that's currently used by the store to calculate the state. It is an advanced API, and may not be required for normal use cases.

 

Redux life cycle


It is quite important to understand the Redux life cycle. To understand the Redux life cycle, you must understand the steps involved in a complete cycle. A user interacts with an interface through some events, like clicking on a button to create some resource. For example, to save a doctor record to a database, the user enters the relevant information and hits the Save button. These events initiate some actions. As we mentioned previously, an action is a pure JavaScript object that tells us what happened.

Redux confirms whether the dispatched action contains the type property. After the confirmation, it is passed the main reducer.This is referred to as dispatching an action. An action is dispatched using the following function:

store.dispatch(action)

The entire concept of how Redux operates is illustrated in the following diagram:

The main reducer function, when called with the current state and dispatched action, passes the sub-states and action down to another reducer. As we mentioned in the previous section, the reducer is just a function, and it uses the previous state and provides the new state. Developers prefer to split the state tree into multiple slices and create a separate reducer for each state slice. Actions, on the other hand, can be concerned with more than one state slice. This method of splitting the reducers into smaller and easier to understand pieces is termed decomposition.

The new state is returned by the main reducer function and is saved in the Redux store, and all listener functions that are subscribed via store.subscribe() get called. This causes the re-render of user interfaces. We will look at the concept of Redux middleware more in Chapter 6, Extending Redux by Middleware.

So far, we have gone over a lot of theoretical concepts. If it does not make sense entirely, do not worry. It takes some time to sync with the concepts and the flow. To get better insight into how this works, let's get started with the very basic concept of making your first Redux Hello World application.

 

Getting started


Let's get started with using Redux. We will start with basic configurations, and we can take the configurations further in each chapter:

  1.  Installing node and npm/yarn is done as follows:

Install the latest version of a node from https://nodejs.org/. To verify the correct installation, run the following command:

node --version

You can get the latest version of yarn from https://yarnpkg.com/en/, and npm from https://www.npmjs.com/get-npm.

  1. Initialize the project as follows:

The first thing is to initialize the project. In this project, we are going to use yarn. The easiest way to initialize the project with package.json is to run the init command:

yarn init
OR
npm init

Follow the onscreen instructions and provide the details. It will ask about the name of the project, the description, the author name, the version, the license, and the entry point. Most of the information can be customized according to your requirements.

Now, create a source folder, src, to include all of our code. The next step is to set up webpack. The minimal package.json file looks like the following:

{
 "name": "gettting-started-with-redux-ch01",
 "version": "1.0.0",
 "description": "Getting Started With Redux",
 "main": "src/app/app.js",
 "scripts": {
   "start": "webpack-dev-server",
   "build": "webpack"
 },
 "author": "Suresh KUMAR Mukhiya",
 "license": "MIT",
 "devDependencies": {
   "@babel/core": "7.2.0",
   "@babel/preset-env": "7.2.0",
   "@babel/preset-react": "7.0.0",
   "babel-core": "6.26.3",
   "babel-loader": "8.0.4",
   "babel-plugin-transform-object-rest-spread": "6.26.0",
   "webpack": "4.27.1",
   "webpack-cli": "3.1.2",
   "webpack-dev-server": "3.1.10"
 },
 "dependencies": {
   "redux": "4.0.1"
 }
}
  1. Configuring webpack is done as follows:

You can read more about webpack on their official documentation site (https://webpack.js.org/). The first thing is to install webpack and webpack-dev-server:

yarn add webpack webpack-dev-server --dev

Now, we need to configure the webpack. We can do that in different ways. A lot of information about webpack configuration can be found on the documentation site (https://webpack.js.org/configuration/). The minimum configuration that we need is as follows:

const path =require('path')
module.exports ={
 Mode: ‘development’,
 entry:'./app/app.js',
 output: {
   path: path.resolve('dist'),
   filename: 'main.js'
 },

Let's place the webpack configuration files into the webpack folder and create a base configuration file, called webpack.config.js. The loaders in the webpack tell the webpack what to do with the entry file(s). We are going to use Babel to transpile our JavaScript files; so, let's define the babel-loader for .js files, as follows:

const path = require("path");
module.exports = {
 mode: "development",
 entry: "./src/app/app.js",
 output: {
   path: path.resolve("dist"),
   filename: "main.js"
 },
 module: {
   rules: [
     {
       test: /\.jsx?$/,
       use: {
         loader: "babel-loader"
       },
       exclude: /node_modules/
     }
   ]
 }
};

Generally, what we have is the bare minimum code required for Webpack configuration. However, in a real application, we would like to compile more resources than just the JavaScript files, including JS files, CSS files, fonts, image files, and others. We can configure these with webpack.

The most standard practice is to split the configuration into two types: development configuration and production configuration. By now, you might have already realized the need for separate configurations. A detailed blog article about this need and its process can be found at https://www.hacksoft.io/blog/split-your-webpack-configuration-development-and-production/. To keep the configuration simple and elegant, we have created three files for webpack; namely, webpack.base.babel.js, webpack.dev.babel.js, and webpack.prod.babel.js. You can find a similar configuration in the starter file in the GitHub repository.

  1. Babel is configured as follows:

We will use Babel to compile JavaScript files. Let's configure it by installing babel and its related libraries:

yarn add @babel/core @babel/preset-env @babel/preset-react babel-core babel --dev --exact
yarn add babel-loader babel-plugin-transform-object-rest-spread --dev --exact

More configuration related to Babel can be found at https://babeljs.io/docs/usage/api/#options.

For the minimum configuration, we will go with creating a babel.config.js file and adding an entry, as follows:

module.exports = {
 presets: [
   [
     "@babel/preset-env",
     {
       modules: false
     }
   ],
   "@babel/preset-react"
 ],
 plugins: ["transform-object-rest-spread"]
};

This file just indicates which libraries we are using in order to compile our JavaScript files. For example, we are going to use a transform-object-rest-spread library to utilize the spread feature. Learn more about this library at https://babeljs.io/docs/en/babel-plugin-proposal-object-rest-spread. We can add other polyfill plugins that we plan to use throughout the project later on. Babel can be configured in multiple ways. We can also create a .babelrc file to configure it. You will find a working example in Chapter 2, Testing.

  1.  Define the entry file:

The entry file indicates the startup file. We will point our entry file to app/app.js. This file contains the main entry codes. This file will be transpiled into main.js by Babel. Secondly, we will create an index.html file, which acts as the entry file for our application:

<!doctype html>
<html lang="en">
<head>
 <!-- The first thing in any HTML file should be the charset -->
 <meta charset="utf-8">
 <!-- Make the page mobile compatible -->
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <!-- Allow installing the app to the homescreen -->
 <meta name="mobile-web-app-capable" content="yes">
 <link rel="icon" href="/favicon.ico" />
 <title>Redux-Book-starter</title>
</head>
<body>
 <div id="root"></div>
</body>
<script src="dist/main.js"></script>
</html>

Also, inside of the app/app.js, we can try to log some text to verify that the configuration is working fine:

console.log("Welcome to Redux Programming");

Now, the last step is to configure the webpack script to run the build. This can be done by placing scripts in the package.json file:

"scripts": {
 "start": "webpack-dev-server",
 "build": "webpack"
},

The first step is to build the files. To do that, simply run yarn build from your command line. Now, we can run the webpack by using the yarn start command from the command line. Note that this is the minimum configuration required to get started with Redux. Our aim here is for you to learn Redux. So, we are going to use starter files for each of the projects, which can be found in the GitHub repository for this book. The starter files, if preconfigured with Babel, Webpack, and Eslint, are ready to be consumed for further development.

  1. Installing Redux:

To get started with Redux, we need to add Redux to our dependencies list, as follows:

yarn add redux --exact

Now, from the project root folder, inside of your command line, run yarn start and open http://localhost:8080 in your favorite browser, checking the console. You should see the log we have in yourapp/app.js.

 

Understanding Redux methods


Let's implement a simple example to turn on and turn off the light. We can build a simple robot that just listens to commands and, based on the commands, performs some actions. For simplicity, suppose that our robot can only understand two commands, as follows:

  1. TURN_ON
  2. TURN_OFF

Now, let's build our robotic function:

const tubeLight = (state = "OFF", action) => {
 switch (action.type) {
   case "TURN_ON":
     return "ON";
   case "TURN_OFF":
     return "OFF";
   default:
     return state;
 }
};

This is a simple JavaScript function that takes the initial state and action as parameters and returns a state. That sounds like something familiar, doesn’t it? Yup; you are right. This is a simple reducer function.

In the first section, you learned Redux's first principle: a single source of truth. Redux provides a function called createStore that takes the main reducer file and creates the store. Let's create a store, as follows:

import { createStore } from "redux";
const store = createStore(tubeLight);

So far, so good. So, what did we do here? We imported the createStore function from the Redux library that is given tubeLight, which is a reducer, as an argument and is saved into a variable called a store. Here, you can recall a functional programming concept. A function can consume another function. Now, as you have already seen, the store has three methods: getState, dispatch, and subscribe. Let's use them.

log the initial state, as follows:

console.log("Initially tubelight is: ", store.getState());

Try to build it, and run it again (yarn build && yarn start). Check the console:

Initially tubelight is:  OFF

Nothing complex, right? We provided the initial state to OFF, and it logged the initial state as OFF. That looks good. Now, let's try to modify the store. In other words, we should instruct the robot to turn on the tubelight. Remember, we can only modify the store by using a dispatch function. Now, we can use that function and log the state, in order to see the state change:

store.dispatch({ type: "TURN_ON" });
console.log("Now tubelight is: ", store.getState());

The output that you get on the console should be as follows:

Now tubelight is:  ON

Now, it makes sense, right? Let's go further and display the state on the browser, rather than on the console. To do that, let's create a button. When we press the button, it should toggle the tubelight state. That is to say, if the tubelight is ON, we turn it off, and vice versa. To make it simple, let's forget about React and use native JavaScript:

const button = document.createElement("button");
button.setAttribute("id", "lightButton");
var text = document.createTextNode("Toggle Light");
button.appendChild(text);
document.body.appendChild(button);

The preceding snippet will create a simple button on the browser, with the text Toggle Light and the ID lightButton.

Now, we need to add an event listener. That is to say, if the tubelight is on, we turn it off by clicking on the button. We can do that as follows:

document.getElementById("lightButton").addEventListener("click", () => {
 if (store.getState() === "ON") {
   store.dispatch({ type: "TURN_OFF" });
 } else {
   store.dispatch({ type: "TURN_ON" });
 }
});

Now, let's render that in the browser, inside of the body tag:

const render = () => {
 document.body.innerText = store.getState();
 document.body.appendChild(button);
};

This will render the initial state of the store. But we need to display when the state changes. To do that, our third method of the store comes into play (subscribe()):

store.subscribe(render);
render();

Now, try to build the app and run it (yarn build && yarn start). Try to click on the button to change the state, and see whether the state is reflected on the browser. Pretty sweet, right? You can find the working example of this code in the GitHub repository, inside CH01/getting-started.

Manually updating the DOM does not scale in a real application. To do so, we use the help of other libraries, such as React. We will configure React with Redux and use it to understand other complex scenarios in Redux.

 

Setting up the project


Having understood the concept of Redux, let's get started with the project that we promised to work with. We will continue to use the project from the getting-started section to build the other components required for the project. Throughout this entire book, we will develop a multilingual hospital management system. Of course, the development of a complete hospital management system is out of the scope of this book, but we are going to get started with a simple one. We are going to have an authentication system and a CRUD (Create, Read, Update, and Delete) of users to get started with.

Configuring the store

Since we know what a store is in Redux, let's get started with creating a store file. We can keep our store in a separate file. Let's call it configureStore.js:

import { createStore, applyMiddleware, compose } from "redux";

import createReducer from "./reducers";

export default function configureStore(initialState = {}, history) {
 const store = createStore(
   createReducer(),
 );

 // Extensions
 store.injectedReducers = {}; // Reducer registry

 return store;
}

Configuring the root reducer

Our root reducers can reside in a reducers.js file. We are going to use the combinedReducers utility function from the Redux library:

import { combineReducers } from "redux";

import history from "utils/history";

export default function createReducer(injectedReducers = {}) {
 const rootReducer = combineReducers({
   ...injectedReducers
 });
 return rootReducer;
}

Configuring our app with Redux

This is the main file, app.js, which will be the main entry file for our project. We are going to put the file inside of app/app.js. You can see that we are using some of the npm packages, including @babel/polyfill, react, react-dom, react-redux, and sanitize.css:

// Needed for redux-saga es6 generator support
import "@babel/polyfill";

// Import all the third party stuff
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import history from "utils/history";
import "sanitize.css/sanitize.css";

// Import root app
import App from "containers/App";

import configureStore from "./configureStore";

// Create redux store with history
const initialState = {};
const store = configureStore(initialState, history);
const MOUNT_NODE = document.getElementById("app");

const render = () => {
 ReactDOM.render(
   <Provider store={store}>
       <App />
   </Provider>,
   MOUNT_NODE
 );
};

render();

Let's briefly go over these packages, as follows:

  • @babel/polyfill (https://babeljs.io/docs/en/babel-polyfill): Babel polyfill has a polyfill that contains a custom regenerator runtime and core-js. In other words, it allows us to consume the full set of ES6 features, beyond syntax changes, including built-in objects like Promises and WeakMap, as well as new static methods, like Array.from or Object.assign.
  • react: We already know what React is. We are going to dive deeper into creating React components in Chapter 6, Extending Redux by Middleware.
  • react-dom: React DOM helps us to glue React and the DOM. When we want to show our React components on the DOM, we need to utilize this ReactDOM.render() function from React DOM. We will discuss these features more in the upcoming chapters.
  • React-redux: This allows us to communicate, in both ways, between React and Redux (https://github.com/reactjs/react-redux). It is a binding between React and Redux that allows us to create containers and listen to the store changes, reflecting that into a presentational component. We will explore container components (smart components) and presentational components (dumb components) in more detail in upcoming chapters.
  • Sanitize.css (https://github.com/csstools/sanitize.css): This is one of the cascading style sheet libraries that yield consistent, cross-browser default styling of HTML elements, as well as useful defaults.

Creating utilities

We used a history object in app.js. We can create history.js inside of the utils folder and create an instance of history. You can learn more about history from https://github.com/ReactTraining/history. In a nutshell, the history library manages the session history everywhere that JavaScript runs:

import createHistory from "history/createBrowserHistory";
const history = createHistory();
export default history;

Creating the first container

Let's create our first container component, inside of app/containers/App/index.js:

import React from 'react';
import HomePage from 'containers/HomePage/Loadable';
export default function App() {
 return (
   <div>
     <HomePage />
   </div>
 );
}

The home page container contains two files, Loadable.js and index.js:

import loadable from 'loadable-components';
export default loadable(() => import('./index'));

The index.js is as follows:

import React, { PureComponent } from 'react';
/* eslint-disable react/prefer-stateless-function */
export default class HomePage extends PureComponent {
 render() {
   return <h1>This is the HomePage Redux-book container!</h1>;
 }
}

The complete code for this project can be found in the GitHub repository, inside of the CH01 starter files. We are going to continue using it in other chapters. Once you have these files up in your editor, we can start to run our first application. To run the application, the first thing to do is install the npm dependencies, as follows:

yarn install
yarn run

The application should start at http://localhost:8080/.

 

Summary


Redux is one of the most popular libraries used for state management in the frontend ecosystem today. In this chapter, we discussed the need for Redux and the principles that make the library stand out. Moreover, we discussed some of the fundamental concepts of functional programming, and how these concepts are used in the development of the Redux library. We also covered some of the Redux ecosystem and the Redux life cycle, and we created an outline for the project. We implemented the bare minimum version of Redux, extending it to create a larger project.

In the next chapter, you will learn about test-driven development, and we will set up the JEST framework for testing. Moreover, we will continue to use the code that we developed in this chapter in the upcoming chapters.

 

Further study


We outlined the basic architecture of Redux and its ecosystem in this chapter. However, we are aware of the fact that it is not easy to understand everything in one go. The following is a list of resources that you can consider to get further knowledge:

  1. https://redux.js.org/.
  2. Learning Redux, by Daniel Bugl, August 2017, Packt Publications.
  3. https://babeljs.io/docs/en/.
  4. React: Tools and Resources, by Michael Wanyoike; Manjunath, M.; Jack Franklin; Swizec Teller; and Ahmed Bouchefra.
  1. Learning React: Functional Web Development with React and Redux, by Alex Banks and Eve Porcello.
  2. Abelson, Harold; Sussman, Gerald Jay (1984). Structure and Interpretation of Computer Programs. MIT Press. Section 1.3, Formulating Abstractions with Higher-Order Procedures. ISBN 0-262-01077-1.
  3. Mukhiya, S. K. and Hoang Hung, K. (2018). An Architectural Style for Single Page Scalable Modern Web Applications, 5(4), 6–13. Retrieved from https://www.ijrra.net/Vol5issue4/IJRRA-05-04-02.pdf.

About the Authors

  • James Lee

    James Lee is a passionate software wizard working at one of the top Silicon Valley-based start-ups specializing in big data analysis. He has also worked at Google and Amazon. In his day job, he works with big data technologies, including Cassandra and Elasticsearch, and is an absolute Docker geek and IntelliJ IDEA lover. Apart from his career as a software engineer, he is keen on sharing his knowledge with others and guiding them, especially in relation to start-ups and programming. He has been teaching courses and conducting workshops on Java programming / IntelliJ IDEA since he was 21. James also enjoys skiing and swimming, and is a passionate traveler.

    Browse publications by this author
  • Tao Wei

    Tao Wei is a passionate software engineer who works in a leading Silicon Valley-based big data analysis company. Previously, Tao worked in big IT companies, including IBM and Cisco. He has intensive experience in designing and building distributed, large-scale systems with proven high availability and reliability. Tao has an MS degree in computer science from McGill University and many years' experience as a teaching assistant in a variety of computer science classes. In his spare time, he enjoys reading and swimming, and is a passionate photographer.

    Browse publications by this author
  • Suresh Kumar Mukhiya

    Suresh Kumar Mukhiya is a PhD candidate, currently affiliated to the Western Norway University of Applied Sciences (HVL). He is a big data enthusiast, specializing in information systems, model-driven software engineering, big data analysis, and artificial intelligence. He has completed a Masters in information systems from the Norwegian University of Science and Technology, along with a thesis in processing mining. He also holds a bachelor's degree in computer science and information technology (BSc.CSIT) from Tribhuvan University, Nepal, where he was decorated with the Vice-Chancellor's Award for obtaining the highest score. He is a passionate photographer and enjoys traveling.

    Browse publications by this author
Book Title
Unlock this full book FREE 10 day trial
Start Free Trial