How to Update State with useReducer Hook in React
The useReducer hook is a powerful tool in React for
managing complex state logic in functional components. This article
provides a straightforward guide on how to update state using
useReducer, explaining the roles of the reducer function,
the dispatch action, and how to trigger state changes with practical
code examples.
Understanding the useReducer Syntax
The useReducer hook is an alternative to
useState. It is local to the component and accepts two main
arguments: a reducer function and an initial state.
const [state, dispatch] = useReducer(reducer, initialState);state: The current state representation.dispatch: A function used to send actions to the reducer to trigger state updates.reducer: A custom function that determines how the state changes based on the action received.initialState: The starting value of your state.
Step 1: Define the Initial State and Reducer Function
The reducer function takes the current state and an
action as arguments and returns the new state. By
convention, actions are objects with a type property (a
string) and an optional payload (additional data).
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'set':
return { count: action.payload };
case 'reset':
return initialState;
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}Step 2: Initialize the Hook in Your Component
Call useReducer inside your React component, passing the
reducer function and the initial state you defined.
import React, { useReducer } from 'react';
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
// JSX goes here
);
}Step 3: Dispatch Actions to Update State
To update the state, call the dispatch function. Pass an
action object containing the type that matches one of the
cases in your reducer switch statement.
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
{/* Updating state by incrementing */}
<button onClick={() => dispatch({ type: 'increment' })}>
Increment
</button>
{/* Updating state by decrementing */}
<button onClick={() => dispatch({ type: 'decrement' })}>
Decrement
</button>
{/* Updating state using a payload */}
<button onClick={() => dispatch({ type: 'set', payload: 10 })}>
Set to 10
</button>
{/* Resetting state */}
<button onClick={() => dispatch({ type: 'reset' })}>
Reset
</button>
</div>
);
}Key Rules for Updating State
- State is Read-Only (Immutable): Do not modify the
existing
stateobject directly inside the reducer. Always return a new object (e.g., return{ ...state, value: newValue }instead ofstate.value = newValue). - Pure Functions: The reducer function must be pure.
It should only calculate the next state based on the inputs
(
stateandaction) and must not trigger side effects like API calls inside the reducer.