JavaScript Development Space

React useReducer Hook: A Guide to State Management

Add to your RSS feed13 February 20253 min read
Mastering React's useReducer Hook: State Management Guide

In React development, useState is ideal for simple state management, but when handling complex state logic with multiple updates, useReducer provides a more structured and maintainable approach. It enables state changes based on dispatched actions, making state management clearer and more predictable.

Understanding useReducer

useReducer follows the Reducer pattern, similar to Redux, using the principle:

state + action = new state

Basic structure:

js
1 const [state, dispatch] = useReducer(reducer, initialState);
  • reducer(state, action): A pure function that receives the current state and action, returning the new state.
  • initialState: The initial state value.
  • dispatch(action): Triggers the reducer to update the state.

Example 1: Counter

js
1 const reducer = (state, action) => {
2 switch (action.type) {
3 case "increment": return { count: state.count + 1 };
4 case "decrement": return { count: state.count - 1 };
5 default: return state;
6 }
7 };
8
9 const Counter = () => {
10 const [state, dispatch] = useReducer(reducer, { count: 0 });
11 return (
12 <div>
13 <p>Count: {state.count}</p>
14 <button onClick={() => dispatch({ type: "increment" })}>+</button>
15 <button onClick={() => dispatch({ type: "decrement" })}>-</button>
16 </div>
17 );
18 };

When to Use useReducer

  1. Complex state management: When managing multiple state values, useReducer prevents excessive nesting compared to useState.
  2. Multiple operations on state: Ideal for scenarios like a shopping cart, where adding, removing, or modifying items requires structured logic.
  3. Performance optimization: Reduces unnecessary re-renders in certain case

Example 2: Shopping Cart Management

js
1 const reducer = (state, action) => {
2 switch (action.type) {
3 case "add":
4 return state.map(item =>
5 item.id === action.id ? { ...item, quantity: item.quantity + 1 } : item
6 );
7 case "remove":
8 return state.map(item =>
9 item.id === action.id ? { ...item, quantity: Math.max(0, item.quantity - 1) } : item
10 );
11 case "updateName":
12 return state.map(item =>
13 item.id === action.id ? { ...item, name: action.newName } : item
14 );
15 default:
16 return state;
17 }
18 };

Example 3: Form State Management

js
1 const reducer = (state, action) => {
2 switch (action.type) {
3 case "change":
4 return { ...state, [action.field]: action.value };
5 case "reset":
6 return action.initialState;
7 default:
8 return state;
9 }
10 };
11
12 const Form = () => {
13 const initialState = { name: "", email: "" };
14 const [state, dispatch] = useReducer(reducer, initialState);
15
16 return (
17 <form>
18 <input
19 type="text"
20 value={state.name}
21 onChange={(e) => dispatch({ type: "change", field: "name", value: e.target.value })}
22 />
23 <input
24 type="email"
25 value={state.email}
26 onChange={(e) => dispatch({ type: "change", field: "email", value: e.target.value })}
27 />
28 <button type="button" onClick={() => dispatch({ type: "reset", initialState })}>Reset</button>
29 </form>
30 );
31 };

Example 4: Managing API Calls State

js
1 const reducer = (state, action) => {
2 switch (action.type) {
3 case "loading":
4 return { ...state, loading: true, error: null };
5 case "success":
6 return { ...state, loading: false, data: action.data };
7 case "error":
8 return { ...state, loading: false, error: action.error };
9 default:
10 return state;
11 }
12 };
13
14 const FetchData = () => {
15 const [state, dispatch] = useReducer(reducer, { data: null, loading: false, error: null });
16
17 useEffect(() => {
18 dispatch({ type: "loading" });
19 fetch("https://api.example.com/data")
20 .then(res => res.json())
21 .then(data => dispatch({ type: "success", data }))
22 .catch(error => dispatch({ type: "error", error }));
23 }, []);
24
25 if (state.loading) return <p>Loading...</p>;
26 if (state.error) return <p>Error: {state.error}</p>;
27 return <pre>{JSON.stringify(state.data, null, 2)}</pre>;
28 };

Summary

useReducer is a powerful alternative to useState when handling complex state logic. Key advantages include:

  • Clear logic: Avoids scattered state updates in useState.
  • Scalability: Easy to add new actions as needed.
  • Performance benefits: Reduces unnecessary re-renders in some scenarios.

If your application requires structured state management, useReducer enhances maintainability and readability, making it an excellent choice for managing complex states in React.

JavaScript Development Space

© 2025 JavaScript Development Space - Master JS and NodeJS. All rights reserved.