useReducer Hook
useReducer is an alternative to useState for managing complex state. It follows the Redux pattern: you dispatch actions, and a reducer function decides how state changes. Use it when:
- State has multiple sub-values that update together
- The next state depends on the previous in complex ways
- You want to keep state logic easy to test independently
Syntax
const [state, dispatch] = useReducer(reducer, initialState);
// reducer: (currentState, action) => newState
// dispatch: call to send an action to the reducer
Complete Example โ Task Manager
const initialState = {
tasks: [],
filter: "all", // "all" | "active" | "done"
loading: false,
};
function taskReducer(state, action) {
switch (action.type) {
case "ADD_TASK":
return {
...state,
tasks: [...state.tasks, { id: Date.now(), text: action.payload, done: false }],
};
case "TOGGLE_TASK":
return {
...state,
tasks: state.tasks.map(t =>
t.id === action.payload ? { ...t, done: !t.done } : t
),
};
case "DELETE_TASK":
return { ...state, tasks: state.tasks.filter(t => t.id !== action.payload) };
case "SET_FILTER":
return { ...state, filter: action.payload };
case "CLEAR_DONE":
return { ...state, tasks: state.tasks.filter(t => !t.done) };
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
function TaskManager() {
const [state, dispatch] = useReducer(taskReducer, initialState);
const visibleTasks = state.tasks.filter(t => {
if (state.filter === "active") return !t.done;
if (state.filter === "done") return t.done;
return true;
});
return (
<div>
<input
onKeyDown={e => {
if (e.key === "Enter" && e.target.value.trim()) {
dispatch({ type: "ADD_TASK", payload: e.target.value.trim() });
e.target.value = "";
}
}}
placeholder="Add task and press Enter"
/>
{["all","active","done"].map(f => (
<button key={f} onClick={() => dispatch({ type: "SET_FILTER", payload: f })}>
{f}
</button>
))}
<ul>
{visibleTasks.map(task => (
<li key={task.id}>
<input type="checkbox" checked={task.done}
onChange={() => dispatch({ type: "TOGGLE_TASK", payload: task.id })} />
{task.text}
<button onClick={() => dispatch({ type: "DELETE_TASK", payload: task.id })}>โ</button>
</li>
))}
</ul>
<button onClick={() => dispatch({ type: "CLEAR_DONE" })}>Clear completed</button>
</div>
);
}