React useReducer Hook: A Guide to State Management
Add to your RSS feed13 February 20253 min read
Table of Contents
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:
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
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 };89 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
- Complex state management: When managing multiple state values, useReducer prevents excessive nesting compared to useState.
- Multiple operations on state: Ideal for scenarios like a shopping cart, where adding, removing, or modifying items requires structured logic.
- Performance optimization: Reduces unnecessary re-renders in certain case
Example 2: Shopping Cart Management
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 } : item6 );7 case "remove":8 return state.map(item =>9 item.id === action.id ? { ...item, quantity: Math.max(0, item.quantity - 1) } : item10 );11 case "updateName":12 return state.map(item =>13 item.id === action.id ? { ...item, name: action.newName } : item14 );15 default:16 return state;17 }18 };
Example 3: Form State Management
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 };1112 const Form = () => {13 const initialState = { name: "", email: "" };14 const [state, dispatch] = useReducer(reducer, initialState);1516 return (17 <form>18 <input19 type="text"20 value={state.name}21 onChange={(e) => dispatch({ type: "change", field: "name", value: e.target.value })}22 />23 <input24 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
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 };1314 const FetchData = () => {15 const [state, dispatch] = useReducer(reducer, { data: null, loading: false, error: null });1617 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 }, []);2425 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.