React applications often need to handle complex states — like deeply nested objects or multiple interrelated values. While useState
works well for simple states, for more intricate logic, useReducer
is a better tool.
In this article, we’ll break down how useReducer
works, when to use it, and how to implement it in your React app — step by step in simple language.
📌 What is useReducer
?
useReducer
is a React Hook that is used for managing state in a more predictable and scalable way than useState
, especially when:
-
The state logic is complex
-
The next state depends on the previous state
-
Multiple pieces of state are updated together
It works similarly to how reducers work in Redux.
🧠 Basic Syntax of useReducer
const [state, dispatch] = useReducer(reducerFunction, initialState);
-
state
→ The current state -
dispatch
→ A function used to send actions (requests to change state) -
reducerFunction
→ A function that tells how to update the state -
initialState
→ The default state
📦 Example Use Case – A Counter with Multiple Actions
Let’s say we want to build a counter with the following features:
-
Increase by 1
-
Decrease by 1
-
Reset to 0
-
Set a custom value
This is already more complex than what useState
comfortably handles. Here's how we can use useReducer
.
✅ Step-by-Step Implementation
1. Setup a React Project (Skip if already done)
npx create-react-app useReducer-example
cd useReducer-example
npm start
2. Create Counter.js
component
import React, { useReducer } from "react";
// Step 3: Define the initial state
const initialState = { count: 0 };
// Step 4: Define the 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 };
case "set":
return { count: action.payload };
default:
return state;
}
}
// Step 5: Build the component
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div style={{ textAlign: "center", marginTop: "50px" }}>
<h2>Counter: {state.count}</h2>
<button onClick={() => dispatch({ type: "increment" })}>+1</button>
<button onClick={() => dispatch({ type: "decrement" })}>-1</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
<button onClick={() => dispatch({ type: "set", payload: 100 })}>
Set to 100
</button>
</div>
);
}
export default Counter;
6. Use this component in App.js
import React from "react";
import Counter from "./Counter";
function App() {
return (
<div>
<Counter />
</div>
);
}
export default App;
🔍 Explanation (Step by Step)
Step 3: initialState
This is the starting value of our state object: { count: 0 }
.
Step 4: reducer
function
This function accepts state
and action
, and returns the new state depending on the action.type
.
Each case
defines what should happen:
-
"increment"
: Adds 1 -
"decrement"
: Subtracts 1 -
"reset"
: Resets to 0 -
"set"
: Sets it to a specific number passed viapayload
Step 5: useReducer
Hook
We pass the reducer function and initial state.
It gives us the current state (state.count
) and dispatch()
to trigger actions.
UI Buttons
Each button calls dispatch()
with a specific action type to update the state.
🛠 Why Use useReducer
Instead of useState
?
Feature | useState | useReducer |
Best For | Simple states | Complex states |
Updates | Direct | Based on actions |
Structure | Minimal | Predictable |
Readability | Decrease with logic | Clear separation of logic |
🔄 Real-World Use Cases
You should consider useReducer
in:
-
Forms with multiple fields and validation
-
Shopping carts with add/remove item logic
-
Toggle systems with various modes (like dark/light themes)
-
Complex UI states like filters, modals, etc.
📘 Bonus: Custom useReducer
with Form
For practice, try building a form with fields like name
, email
, and message
, and manage them with useReducer
.
🚀 Conclusion
useReducer
gives you clear control over complex state transitions in React apps. It’s an ideal alternative to useState
when your state management becomes too intricate or tightly coupled. Once you get the hang of it, you’ll find your components easier to test, debug, and scale.