React useReducer Hook
Written By: Avinash Malhotra
Updated on
In React, we usually use the useState hook to manage state. But when the state logic becomes complex — for example, when multiple values are related or the next state depends on the previous one — the useReducer hook is a better choice.
useState is great for simple state management, but it can become cumbersome when dealing with complex state transitions.
useReducer is designed to handle more complex state logic in a more predictable way. It gives you more control and makes state updates predictable and easier to manage.
What is useReducer Hook?
useReducer is an alternative to useState.
It's commonly used when you have complex state logic or need centralized state management inside a component.
Here's an example:
const [state, dispatch] = useReducer(reducer, initialState);
- state → the current state value
- dispatch → a function to update the state
- reducer → a function that defines how the state should be updated
- initialState → the initial state value
Example
import { useReducer } from "react";
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 "reset":
return { count: 0 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
</div>
);
}
In this example, we define a reducer function that handles three actions: increment, decrement, and reset. The Counter component uses the useReducer hook to manage the count state. The dispatch function is called with an action object to update the state based on the action type.
Managing Complex States
useReducer is particularly useful when managing complex states that involve multiple related values or when the next state depends on the previous state.
For example, consider a form with multiple fields where the state needs to be updated based on user input:
import { useReducer } from "react";
const initialState = { name: "", email: "" };
function reducer(state, action) {
switch (action.type) {
case "setName":
return { ...state, name: action.value };
case "setEmail":
return { ...state, email: action.value };
case "reset":
return initialState;
default:
return state;
}
}
function FormExample() {
const [state, dispatch] = useReducer(reducer, initialState);
const handleSubmit = (e) => {
e.preventDefault();
console.log(state);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={state.name}
placeholder="Enter name"
onChange={(e) =>
dispatch({ type: "setName", value: e.target.value })
}
/>
<br />
<input
type="email"
value={state.email}
placeholder="Enter email"
onChange={(e) =>
dispatch({ type: "setEmail", value: e.target.value })
}
/>
<br />
<button type="submit">Submit</button>
<button type="button" onClick={() => dispatch({ type: "reset" })}>Reset</button>
</form>
);
}
In this example, we manage the state of a form with two fields: name and email. The reducer function handles actions to update each field and reset the form. This approach keeps the state management organized and makes it easier to handle complex interactions.
When to use useReducer
Consider using useReducer in the following scenarios:
- When you have complex state logic that involves multiple sub-values.
- When the next state depends on the previous state.
- When you want to optimize performance for components that trigger deep updates.
- Works great for forms, counters, and multi-step components.
When to use useState
Consider using useState in the following scenarios:
- When you have simple state logic that involves a single value.
- When you want to manage local component state without complex interactions.