JavaScript Development Space

React useReducer Hook: A Guide to State Management

13 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

JSDev Space – Your go-to hub for JavaScript development. Explore expert guides, best practices, and the latest trends in web development, React, Node.js, and more. Stay ahead with cutting-edge tutorials, tools, and insights for modern JS developers. 🚀

Join our growing community of developers! Follow us on social media for updates, coding tips, and exclusive content. Stay connected and level up your JavaScript skills with us! 🔥

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