Building scalable and maintainable React applications with Redux: Create an e-commerce app with me

Piyush Singh
6 min readFeb 12, 2023

React and Redux are two of the most popular libraries in the JavaScript ecosystem. React is a library for building user interfaces, while Redux is a state management library that helps manage the state of your application.

The combination of React and Redux can be used to build complex and scalable web applications, and this article will guide you through the process of building a simple e-commerce application using these technologies.

React is a component-based library, which means that you build applications by composing individual components that each manage their own state. Redux, on the other hand, provides a centralized store for managing the state of your entire application. This means that you can easily manage the state of your application in a single place, making it easier to manage and maintain your codebase.

In this article, we will walk through the steps of building an e-commerce application using React and Redux. The application will consist of two main components: a product list and a shopping cart. The product list will display a list of products that the user can add to their cart, and the shopping cart will display the items that the user has added.

The first step in building our application is to set up a new React project. You can use Create React App to quickly set up a new project with all the necessary dependencies.

npx create-react-app my-app
cd my-app
npm start

Once your project is set up, you can start building the product list component. This component will display a list of products and allow the user to add items to their cart.

import React, { useState } from 'react';

const ProductList = () => {
const [products, setProducts] = useState([
{ id: 1, name: 'Product 1', price: 9.99 },
{ id: 2, name: 'Product 2', price: 19.99 },
{ id: 3, name: 'Product 3', price: 29.99 },
]);
const [cart, setCart] = useState([]);
const addToCart = (product) => {
setCart([...cart, product]);
};
return (
<div>
<h2>Product List</h2>
<ul>
{products.map((product) => (
<li key={product.id}>
{product.name} - ${product.price}
<button onClick={() => addToCart(product)}>Add to Cart</button>
</li>
))}
</ul>
</div>
);
};
export default ProductList;

Next, we will build the shopping cart component, which will display the items that the user has added to their cart.

import React from 'react';

const ShoppingCart = ({ cart }) => {
return (
<div>
<h2>Shopping Cart</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{cart.map((item) => (
<tr key={item.id}>
<td>{item.name}</td>
<td>${item.price}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};

export default ShoppingCart;

Finally, we will integrate these two components into our main App component and render them to the page.

import React from 'react';
import ProductList from './ProductList';
import ShoppingCart from './ShoppingCart';

const App = () => {
return (
<div>
<ProductList />
<ShoppingCart />
</div>
);
};
export default App;

Now, if you run your application, you should see a product list and a shopping cart that allows you to add items to your cart. However, this solution is not scalable and maintainable, as the state of our application is stored in two separate components. To solve this problem, we can use Redux to manage the state of our application in a single place.

First, we need to install the redux and react-redux libraries.

npm install redux react-redux

Next, we will create a Redux store and initialize it in our main App component.

import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import ProductList from './ProductList';
import ShoppingCart from './ShoppingCart';

const initialState = {
products: [
{ id: 1, name: 'Product 1', price: 9.99 },
{ id: 2, name: 'Product 2', price: 19.99 },
{ id: 3, name: 'Product 3', price: 29.99 },
],
cart: [],
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TO_CART':
return { ...state, cart: [...state.cart, action.product] };
default:
return state;
}
};
const store = createStore(reducer);
const App = () => {
return (
<Provider store={store}>
<ProductList />
<ShoppingCart />
</Provider>
);
};
export default App;

Now, we can connect our ProductList and ShoppingCart components to the Redux store using the connect function from react-redux.

import React from 'react';
import { connect } from 'react-redux';

const ProductList = ({ products, addToCart }) => {
return (
<div>
<h2>Product List</h2>
<ul>
{products.map((product) => (
<li key={product.id}>
{product.name} - ${product.price}
<button onClick={() => addToCart(product)}>Add to Cart</button>
</li>
))}
</ul>
</div>
);
};

const mapStateToProps = (state) => ({
products: state.products,
});

const mapDispatchToProps = (dispatch) => ({
addToCart: (product) =>
dispatch({ type: 'ADD_TO_CART', product: product }),
});

export default connect(mapStateToProps, mapDispatchToProps)(ProductList);
import React from 'react';
import { connect } from 'react-redux';

const ShoppingCart = ({ cart }) => {
return (
<div>
<h2>Shopping Cart</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{cart.map((item) => (
<tr key={item.id}>
<td>{item.name}</td>
<td>${item.price}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};

const mapStateToProps = (state) => ({
cart: state.cart,
});

export default connect(mapStateToProps)(ShoppingCart);

Now, our components are connected to the Redux store and are able to access the state and dispatch actions. This solution is much more scalable and maintainable, as we only have one source of truth for our application’s state and all updates to the state are handled through actions and reducers in a centralized way.

This is just a basic example of how you can use Redux to build scalable and maintainable React applications. In a real-world application, you would likely have many more components and actions, and you would use middleware such as redux-thunk or redux-saga to handle asynchronous actions, such as fetching data from an API.

Additionally, you would want to split up your reducers into smaller, more manageable pieces, using the combineReducers function from the redux library. This allows you to keep your code organized and makes it easier to maintain as your application grows in complexity. You would also want to implement proper error handling and debugging tools, such as the redux-logger middleware.

In terms of testing, you can use a testing library such as jest and enzyme to write unit tests for your actions and reducers, as well as integration tests for your components to ensure that everything is working as expected. This helps to catch any bugs or issues early on in the development process, before they become bigger problems.

Finally, it’s important to keep your code organized and easy to understand, even as your application grows in size. This includes writing clean, readable code and commenting on your code to explain what it does. You should also follow a consistent coding style and adopt best practices, such as using functional components instead of class components and keeping your components small and reusable.

By following these best practices and using Redux to manage your application’s state, you can build scalable and maintainable React applications that are easy to update and debug, even as they grow in complexity.

In conclusion,

using Redux in your React applications provides several benefits, including:

  • A single source of truth for your application’s state, making it easier to manage and debug
  • Centralized handling of updates to the state, reducing the risk of bugs and inconsistencies
  • Improved scalability, as your application grows in complexity, you can split up your state and actions into smaller, more manageable pieces
  • Improved maintainability, as your code is organized and easy to understand, making it easier to update and debug
  • Improved testing, as you can write unit and integration tests for your actions, reducers, and components to catch bugs early on in the development process

By following best practices and using Redux in combination with React, you can build robust, scalable, and maintainable applications that are easy to update and debug, even as they grow in complexity.

--

--