The useReducer Hook is used to manage state in React,
especially when state logic is more complex than a simple
useState update. It works with a reducer function
and actions.
useReducer Hook is useful when
multiple state updates are related or when the state transition logic
needs to be organized in one place.
useReducer is a React Hook used for managing state through
a reducer function.
const [state, dispatch] =
useReducer(reducer, initialState);
It returns the current state and a dispatch function.
Before using useReducer, import it from React.
import { useReducer } from "react";
You can then use the Hook inside a functional component.
const [state, dispatch] =
useReducer(reducer, initialState);
There are three important parts:
state – the current statedispatch – sends an actionreducer – decides how the state changesA reducer is a function that receives the current state and an action and returns the next state.
function reducer(state, action) {
return newState;
}
The reducer contains the logic used to update the state.
An action describes what happened or what operation should be performed.
{
type: "increment"
}
The reducer checks the action type and decides what state should be returned.
The dispatch function sends an action to the reducer.
dispatch({
type: "increment"
});
React passes the action to the reducer, which calculates the next state.
import { useReducer } from "react";
function reducer(state, action) {
if (action.type === "increment") {
return state + 1;
}
return state;
}
function Counter() {
const [count, dispatch] =
useReducer(reducer, 0);
return (
<div>
<h2>
Count: {count}
</h2>
<button
onClick={() =>
dispatch({
type: "increment"
})
}
>
Increase
</button>
</div>
);
}
export default Counter;
The second argument of useReducer is the initial state.
const initialState = 0;
const [count, dispatch] =
useReducer(reducer, initialState);
The initial state is used when the component starts.
A switch statement is commonly used to handle different
action types.
function reducer(state, action) {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return state;
}
}
function reducer(state, action) {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return state;
}
}
Different actions can perform different state transitions.
<button
onClick={() =>
dispatch({
type: "increment"
})
}
>
+
</button>
<button
onClick={() =>
dispatch({
type: "decrement"
})
}
>
-
</button>
Each button sends a different action to the reducer.
A reducer can also handle a reset operation.
function reducer(state, action) {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
case "reset":
return 0;
default:
return state;
}
}
An action can contain additional data, usually inside a
payload property.
dispatch({
type: "add",
payload: 5
});
The reducer can use this value to calculate the next state.
function reducer(state, action) {
switch (action.type) {
case "add":
return state + action.payload;
default:
return state;
}
}
Here, action.payload contains the amount to add.
useReducer is especially useful when the state contains
multiple related values.
const initialState = {
name: "",
age: 0,
email: ""
};
The reducer can update individual properties based on actions.
function reducer(state, action) {
switch (action.type) {
case "setName":
return {
...state,
name: action.payload
};
default:
return state;
}
}
The spread operator keeps the other state properties unchanged.
function reducer(state, action) {
switch (action.type) {
case "setName":
return {
...state,
name: action.payload
};
case "setAge":
return {
...state,
age: action.payload
};
case "setEmail":
return {
...state,
email: action.payload
};
default:
return state;
}
}
A reducer can manage multiple form fields from one state object.
const initialState = {
username: "",
email: "",
password: ""
};
Actions can update each field in a predictable way.
<input
value={state.username}
onChange={(e) =>
dispatch({
type: "username",
payload: e.target.value
})
}
/>
The input value is sent to the reducer through an action.
| useState | useReducer |
|---|---|
| Simple state updates. | More structured state logic. |
| Uses a setter function. | Uses a reducer and dispatch. |
| Good for simple values. | Useful for related or complex state transitions. |
| Easy for small components. | Useful when many actions affect the same state. |
useReducer can be useful when:
A reducer should calculate the next state from the current state and the action.
function reducer(state, action) {
switch (action.type) {
case "increment":
return state + 1;
default:
return state;
}
}
The reducer should not perform unrelated side effects.
State should be updated by returning a new state instead of directly mutating the existing state object.
Avoid:
state.name = "Rahul";
return state;
Prefer:
return {
...state,
name: "Rahul"
};
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>
<h2>
Count: {state.count}
</h2>
<button
onClick={() =>
dispatch({
type: "increment"
})
}
>
Increase
</button>
<button
onClick={() =>
dispatch({
type: "decrement"
})
}
>
Decrease
</button>
<button
onClick={() =>
dispatch({
type: "reset"
})
}
>
Reset
</button>
</div>
);
}
export default Counter;
const initialState = {
count: 0,
message: "Ready"
};
function reducer(state, action) {
switch (action.type) {
case "increment":
return {
count: state.count + 1,
message: "Count increased"
};
case "decrement":
return {
count: state.count - 1,
message: "Count decreased"
};
default:
return state;
}
}
One reducer can manage several related values together.
useReducer can be combined with Context when application
state needs to be shared by many components.
const [state, dispatch] =
useReducer(
reducer,
initialState
);
<AppContext.Provider
value={{
state,
dispatch
}}
>
<App />
</AppContext.Provider>
This pattern is useful for organizing shared application state.
Action types should clearly describe the operation being performed.
"increment"
"decrement"
"reset"
"setName"
"setEmail"
"addItem"
"removeItem"
Clear action names make reducer logic easier to understand.
useReducer.
The basic workflow of useReducer is:
useReducer().User Action
↓
dispatch()
↓
Action
↓
Reducer
↓
New State
↓
Re-render
useReducer for complex state logic.useReducer is a React Hook for managing state with a reducer function.dispatch() sends an action to the reducer.type property.payload.useReducer is useful for complex or related state updates.useReducer can be combined with Context for shared application state.
Question: What does the dispatch function do in
useReducer?