This example code adds items to a cart using react-redux with higher order components (HOC). Let us begin with the redux workflow explanation.

The work flow for redux is as follows:

Diagram-1: Redux work flow

There’s some action that goes into our Root-Reducer which propagates changes the Store, which results in DOM changes. Before an action hits the Root-Reducer, it can hit a Middleware. Middleware is just some code that gets the action before the Root-Reducer. We use the Logger Middleware in order to test our code as we develop. To add this Middleware as well as Redux to the project, add the following code to the command line:

yarn add redux redux-logger react-redux

We get a component called Provider from “react-redux”, which we will wrap around the entire project. To do this, we add it to the index.js file:

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';

As you can see the <Provider> is wrapped around the entire project, which has access to the store object that we get from redux. Provider acts as the parent to everything wrapped around inside of the component. Now that we have the Provider we have to write our store and root-reducer.

The root-reducer is the actual base reducer object that represents all of the state of our application.

root-reducer.js:

import { combineReducers } from 'redux';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

This Root-Reducer will become the actual code that combines all of our other states together. We do this so that we break the code up into it’s individual sections so that it isn’t just one giant file.

We then need to make the reducer for which ever action we are going to take. For this example, we will work on things dealing with adding multiple items to the cart. Next we have to separate the file structure to the specific action that you are dealing with. In our case, we are going to be dealing with the cart, so we will write the cart.reducer.js file.

cart.reducer.js:

import CartActionTypes from './cart.types';
import { addItemToCart, removeItemFromCart } from './cart.utils';

Reducers are just a function that get two properties. It gets a state object which represents the last state or an initial state which is just an object of what it is that we are trying to store. It then receives an action which is just an object that has a type of action to follow. It also comes with a payload which we could use to update the state.

Notice that the root-reducer imports the cart reducer as well as other reducers written for the project. These reducers all get combined using combineReducers which is imported from redux. Since we made our root-reducer, as well as reducer, we need to create our store.js.

store.js:

import { createStore, applyMiddleware } from 'redux';
import { persistStore } from 'redux-persist';
import logger from 'redux-logger';

Looking back at the Diagram-1, we see that we need to add Middleware to the Store so that the actions get dispatched we can catch them and display them. Middleware which is in between the actions firing and Root-Reducer are just functions that receive actions in, does something with them and then pass them down to the Root-Reducer. Once the store is created, we add it to the Provider which is seen inside of the code under index.js

Once this is all done, we need to create the action creator files (which we saw being used in cart.reducer.js.)

cart.actions.js:

import CartActionTypes from './cart.types';

These are just functions that return objects. Each object is in the correct format that the action is expected to be.

In our reducer, and action files we see that we are using the same types of hard coded strings. For best practices we want to make it a consistent reference so that if it gets updated or has a spelling mistake, you only have to go to one place in order to make sure the types are correct. So we create the file cart.types.js. This will be imported inside of the action file and reducer files when needed.

cart.types.js:

const CartActionTypes = {
TOGGLE_CART_HIDDEN: "TOGGLE_CART_HIDDEN",
ADD_ITEM: "ADD_ITEM",
REMOVE_ITEM: "REMOVE_ITEM",
CLEAR_ITEM_FROM_CART: "CLEAR_ITEM_FROM_CART",
CLEAR_CART: 'CLEAR_CART'
}

Next we need to introduce the higher order component connect. Higher order components are just functions that take two components as arguments and then return you a new component.

collection.component.jsx:

import React from 'react';
import { connect } from 'react-redux';
import CollectionItem from '../../components/collection-item/collection-item.component';

For connect, the first input function that allows us to access the state, with the state being our Root-Reducer. This is standardly named mapStateToProps where the name of the property will be the same as the property we are passing in and the value being the value. The pattern you see with connect, mapStateToProps will be used anywhere we need properties from the reducers.

we also need to look at the second argument for connect, which is mapDispatchToProps. mapDispatchToProps updates the values in the root-reducer, which then updates the other reducers when using that updated data.

collection.items.jsx:

import React from 'react'

In order to update the values in the reducers we will use mapDispatchToProps, which is the second argument of connect. This will get dispatch property and return an object where the prop name of whatever name we want to use inside of passed into the cart.action.js file which is imported into the file (In our case it is addItem.

Now we need to create the functions which will be called on the reducer code. We create a cart.utils.js file that holds functions which are imported inside of cart.reducer.js file.

cart.utils.js:

export const addItemToCart = (cartItems, cartItemToAdd) => {
const existingCartItem = cartItems.find(cartItem => cartItem.id === cartItemToAdd.id)

This file is where we create any utility functions that we need. It allows us to keep our files clean and organized functions that we could use in multiple locations throughout the development of our code.

Now we can write a selector. A selector is code that gets the whole state object but then pull off a small portion of the relevant state when needed. We create a cart.selectors.js file.

cart.selectors.js:

import { createSelector } from 'reselect'

The function reduce() re-renders every time the state is changed, so writing code inside of the component is slowing down the performance of the project due to it having to go into mapStateToProps for the data to be updated. So we create a cart.selector.js file with all of the functions needed, and this targets specific states so that less re-rendering is needed as users interact with the website.

Next we can import the needed data inside of a checkout.component.jsx file.

checkout.component.jsx:

import React from 'react'
import { connect } from 'react-redux'
import { createStructuredSelector } from 'reselect'
import { selectCartItems, selectCartTotal} from '../../redux/cart/cart.selectors'
import CheckoutITem from '../../components/checkout-item/checkout-item.component'
import StripeCheckoutButton from '../../components/stripe-button/stripe-button.component'
import './checkout.style.scss'

In this code you can see that mapStateToProps is given the cartItems and total the properties from the cart.selectors which is imported. This can then be used to display that data on the checkout page.

So there we go, implementing redux using HOC into the cart which updates when items that are added to it in the project. But you have to ask yourself the question as to when do I use this and why would I need to go through the trouble of setting all of these files and methods up?

Redux allows for large scale application where you would want an ecosystem supporting it that is more flushed out. The support and development of redux gives you a lot more flexibility including the async event handling and ability to reuse components in a much better way.

If you are building a small to medium project such as portfolio project or landing page (not that much reusable code, or needing many state management) using something like context api would be your better bet. You wont have have to introduce files and folder structures for redux for state management. So decide on what is best for the development of your project.

If you are interested in seeing the full project for this website click here! Or if you would like to get the full code of this project using Redux with HOC, check out my Github repo! If you have any other questions, please feel free to contact me here on my Linkedin!

Problem solver with experience in Software, and Mechanical engineering. Interested in web-dev, machine learning, artificial intelligence, and new technologies.