Hey everyone! đ
Weâve all been there with useState. Itâs simple, itâs clean, and it gets the job done for most things. But then⌠your component grows.
Suddenly you have five different useState hooks, and updating one requires checking three others. Your handleUpdate function looks like a bowl of spaghetti. đ
Enter useReducer.
It sounds scary because it reminds people of Redux (which can be intimidating), but trust me, useReducer is actually quite friendly once you get to know it.
Letâs break it down.
What is useReducer?
Think of useState like a light switch. You flip it on, you flip it off. Simple direct control.
useReducer is more like an Instruction Manual for your state. Instead of just saying âChange value to Xâ, you say âHey React, apply the âINCREMENTâ instructionâ.
It allows you to move the logic of how state changes OUTSIDE of your component.
1. The Setup
To use it, you need three things:
- The State: Your data (e.g.,
{ count: 0 }). - The Action: A message describing what happened (e.g.,
{ type: 'INCREMENT' }). - The Reducer: A function that takes current state and an action, and returns the NEW state.
Letâs look at the Reducer function first.
// This function doesn't need to live inside the component!
const 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;
}
};
See? Itâs just a pure JavaScript function. No React magic inside. It just takes (state, action) and returns newState.
2. Using the Hook
Now, letâs wire it up in our component.
import { useReducer } from "react";
// reuse the reducer function from above
// const reducer = ...
const Counter = () => {
// 1. Initialize the hook
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
{/* 2. Dispatch actions instead of setting state directly */}
<button onClick={() => dispatch({ type: "DECREMENT" })}>-</button>
<button onClick={() => dispatch({ type: "RESET" })}>Reset</button>
<button onClick={() => dispatch({ type: "INCREMENT" })}>+</button>
</div>
);
};
Notice we donât say setCount(count + 1). We say dispatch({ type: "INCREMENT" }).
We are sending a message. The reducer handles the logic.
Why bother? This looks like more code!
Youâre right. For a simple counter, useState is better.
But useReducer shines when:
- State is complex: You have an object with deeply nested values.
- Updates depend on other state: e.g., âWhen âStatusâ changes to âSuccessâ, also clear âErrorâ and set âLoadingâ to falseâ.
- Testing: You can test the
reducerfunction in isolation without rendering the component!
Real World Scenario: A Form
Imagine a form with name, email, loading, and error.
With useState, you might have 4 hooks. Or one big object that you have to spread spread spread ...state every time.
With useReducer:
// One update handles multiple changes cleanly
case "LOGIN_SUCCESS":
return {
...state,
loading: false,
user: action.payload,
error: null
};
It keeps your event handlers clean. Your handleSubmit just dispatches one action, and the reducer handles the mess.
Closing
Donât be afraid of useReducer. You donât have to use it everywhere.
- Start with
useState. - If your state logic starts looking like spaghetti codeâŚ
- Refactor to
useReducer.
Itâs a powerful tool in your React utility belt. đ ď¸
Happy Coding! đ