React useReducer Hook
In React, the useReducer hook provides a powerful alternative to useState for managing complex or nested state logic. It’s particularly useful when your component has multiple related state variables or when state updates depend on previous state values.
While useState is perfect for simple state updates, useReducer brings predictable state transitions and centralized logic, making your code cleaner and easier to maintain.
What is useReducer?
The useReducer hook works similarly to reducers in Redux:
- You define a reducer function that takes the current state and an action.
- The reducer returns the new state based on the action.
- You use
dispatchto send actions to the reducer, updating state.
Syntax:
const [state, dispatch] = useReducer(reducer, initialState);
state→ Current state valuedispatch→ Function to send actions to the reducerreducer→ Function that calculates the next stateinitialState→ Initial state value
Example:
import React, { useReducer } from "react";
// Reducer function
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
case "reset":
return { count: 0 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<h1>Count: {state.count}</h1>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
</div>
);
}
export default Counter;
When to Use useReducer
useReducer is ideal when:
- State depends on previous values.
- You have multiple related state variables.
- You want centralized state logic instead of multiple
useStatecalls. - You need predictable updates similar to Redux but without external libraries.
Example – Multiple State Management:
const initialState = { count: 0, text: "" };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { ...state, count: state.count + 1 };
case "setText":
return { ...state, text: action.payload };
default:
return state;
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h2>Count: {state.count}</h2>
<inputtype="text"
value={state.text}
onChange={(e) => dispatch({ type: "setText", payload: e.target.value })}
/>
<button onClick={() => dispatch({ type: "increment" })}>Increment</button>
</div>
);
}
Benefits of useReducer:

- Centralized State Logic – All state updates are handled in the reducer function.
- Predictable Updates – Clear action types and state changes make debugging easier.
- Better for Complex State – Ideal when
useStatebecomes hard to manage. - Easier Testing – Reducer functions are pure and can be tested independently.
Comparison: useState vs useReducer
| Feature | useState | useReducer |
|---|---|---|
| Best For | Simple state | Complex or multiple related state |
| State Updates | Directly using setter | Through actions dispatched to reducer |
| State Logic Location | Inline in component | Centralized in reducer function |
| Scaling | Harder for complex state | Easier to maintain and scale |
