Exploring the Power of useReducer: A Complex State Management Example

Ashraful-Mijan
3 min readJun 28, 2023

Introduction:

React’s useReducer hook provides a powerful alternative to useState for managing complex state in your applications. While useState is suitable for simple state management scenarios, useReducer shines when dealing with more complex state transitions and business logic. In this tutorial, we'll dive into a practical example to showcase why useReducer is a better choice for managing intricate state requirements.

Step 1: Setting up the Project Before we begin, let’s create a new React project. Open your terminal and run the following commands:

npx create-react-app use-reducer-tutorial
cd use-reducer-tutorial

Step 2: Creating a Shopping Cart Component To illustrate the benefits of useReducer, we'll build a shopping cart component that handles adding and removing items. Create a new file called ShoppingCart.js in the src directory and add the following code:

import React, { useReducer } from 'react';

const initialState = {
items: [],
total: 0,
};

//reducer function
const reducer = (state, action) => {
switch (action.type) {
case 'ADD_ITEM':
return {
...state,
items: [...state.items, action.payload],
total: state.total + action.payload.price,
};
case 'REMOVE_ITEM':
const updatedItems = state.items.filter(
(item) => item.id !== action.payload.id
);
return {
...state,
items: updatedItems,
total: state.total - action.payload.price,
};
default:
return state;
}
};

// component
const ShoppingCart = () => {
const [state, dispatch] = useReducer(reducer, initialState);

const addItem = (item) => {
dispatch({ type: 'ADD_ITEM', payload: item });
};

const removeItem = (item) => {
dispatch({ type: 'REMOVE_ITEM', payload: item });
};

return (
<div>
<h2>Shopping Cart</h2>
<ul>
{state.items.map((item) => (
<li key={item.id}>
{item.name} - ${item.price}
<button onClick={() => removeItem(item)}>Remove</button>
</li>
))}
</ul>
<p>Total: ${state.total}</p>
<button onClick={() => addItem({ id: 1, name: 'Product 1', price: 10 })}>
Add Product 1
</button>
<button onClick={() => addItem({ id: 2, name: 'Product 2', price: 20 })}>
Add Product 2
</button>
</div>
);
};

export default ShoppingCart;

Step 3: Testing the ShoppingCart Component To ensure everything is set up correctly, open the src/App.js file and replace the existing code with the following:

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

function App() {
return (
<div className="App">
<ShoppingCart />
</div>
);
}

export default App;

Save the file and start the development server by running npm start in the terminal. You should see a shopping cart component rendered in your browser.

Step 4: Understanding the Code Let’s go through the changes we made in the ShoppingCart.js file:

  • We defined an initialState object representing the initial state of the shopping cart, with items as an empty array and total set to zero.
  • Created a reducer function that handles state transitions based on the dispatched action type. In this case, we handle 'ADD_ITEM' and 'REMOVE_ITEM' actions by updating the items array and recalculating the total price.
  • Initialized state and dispatch by calling useReducer(reducer, initialState). state holds the current state object, and dispatch is a function used to send actions to the reducer.
  • Updated the addItem and removeItem functions to dispatch the corresponding actions along with the payload (item data).

Step 5: Testing the Updated ShoppingCart Component Save the ShoppingCart.js file, and you should see the shopping cart component rendered in your browser. You can test the functionality by clicking the "Add Product 1" and "Add Product 2" buttons to add items to the cart. Each item will be displayed with its name, price, and a "Remove" button. Clicking the "Remove" button will remove the corresponding item from the cart, and the total price will be updated accordingly.

Conclusion: In this tutorial, we explored the benefits of using useReducer over useState for managing complex state in React applications. By leveraging the power of reducers and dispatching actions, we created a shopping cart component capable of handling item additions and removals while keeping track of the total price. The useReducer hook provides a scalable and maintainable solution for complex state management scenarios. Remember to consider your project's specific needs and state complexity when choosing between useState and useReducer. Happy coding!

--

--