React useReducer Hook
25 April 2025 | Category: React Js
The useReducer
Hook in React is like an advanced version of useState
. It’s used for managing more complex state logic, especially when:
- Multiple values are connected
- State updates depend on previous state
- You want a Redux-like reducer pattern without using Redux
🔧 Why use useReducer
?
If useState
is like a toolbox, then useReducer
is like a workshop – more organized, powerful, and scalable.
Use it when:
- You have multiple related pieces of state
- You need clear state transitions
- You want predictable logic using
action
andtype
🧪 Syntax
const [state, dispatch] = useReducer(reducerFunction, initialState);
state
: current state object or valuedispatch
: function to send actions to the reducerreducerFunction
: function that handles state updatesinitialState
: the starting value of the state
📦 Importing useReducer
import { useReducer } from 'react';
🔁 useReducer vs useState
Feature | useState | useReducer |
---|---|---|
Simple state | ✅ Easy | ⚠️ Overkill |
Complex logic | ❌ Can get messy | ✅ Cleaner with reducer |
Multiple states | ❌ Scattered | ✅ Centralized |
Redux-like workflow | ❌ Not possible | ✅ Very similar |
✅ Example 1: Counter with useReducer
Let’s build a simple counter to understand how useReducer
works.
🧠 Step 1: Reducer function
function counterReducer(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;
}
}
🧠 Step 2: Component using the reducer
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function Counter() {
const [state, dispatch] = useReducer(counterReducer, initialState);
return (
<div>
<h2>Count: {state.count}</h2>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
</div>
);
}
🧠 Explanation:
- We pass an action object to
dispatch
, like{ type: 'INCREMENT' }
- The reducer function updates the state based on the
type
✅ Example 2: Todo List with useReducer
🧠 Step 1: Reducer function
function todoReducer(state, action) {
switch (action.type) {
case 'ADD':
return [...state, { id: Date.now(), text: action.payload }];
case 'DELETE':
return state.filter(todo => todo.id !== action.payload);
default:
return state;
}
}
🧠 Step 2: Component
import React, { useReducer, useState } from 'react';
function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
const [input, setInput] = useState('');
const handleAdd = () => {
if (input.trim()) {
dispatch({ type: 'ADD', payload: input });
setInput('');
}
};
return (
<div>
<h2>Todo List</h2>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter a todo"
/>
<button onClick={handleAdd}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}{' '}
<button onClick={() => dispatch({ type: 'DELETE', payload: todo.id })}>
Delete
</button>
</li>
))}
</ul>
</div>
);
}
🛠 Benefits of useReducer
✅ Organizes complex state logic
✅ Clean separation of logic and UI
✅ Predictable state updates
✅ Scalable – great for large apps
📋 Summary
Concept | Description |
---|---|
useReducer | Hook to manage complex state logic |
state | Current state value |
dispatch() | Function to send actions to the reducer |
reducer() | Pure function to handle state changes |
action | Object with a type and optional payload |
🔥 Bonus Tips
- Reducers should be pure functions (no API calls, no side effects).
- Combine
useReducer
withuseContext
for global state management. - Works great for form state, toggles, checkboxes, dynamic lists, and more.