Let’s stop pretending useReducer
is some architectural upgrade over useState
.
It’s not.
In the hands of most developers, it’s just overengineering theater. A costume.
An attempt to cosplay as a systems thinker in a CRUD world.
But here’s the truth:
If you’re reaching for
useReducer
in your React component, you're probably just adding ceremony to avoid writingsetState
.
Let’s talk about it.
🧠 Why Devs Fall for useReducer
It looks structured.
It feels scalable.
It smells testable.
It lets you write fancy things like:
dispatch({ type: 'UPDATE_FIELD', field: 'title', value: e.target.value });
And now you feel like you're writing a framework instead of a component.
But you're not building a state machine.
You're updating a form input.
This isn't clever. It's LARPing.
💬 The Famous Interview Trauma
You've heard it:
“My friend was rejected from a frontend role because they used useState instead of useReducer 😞”
That anecdote makes the rounds every 3 months — usually paired with something like this:
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
Then comes the critique:
Too many states!
No validation!
Inconsistent logic!
Not enterprisey enough!
So they slap on a reducer:
const [event, updateEvent] = useReducer((prev, next) => {
// This section will be bad for
// your mental health in the real world
const newEvent = { ...prev, ...next };
if (newEvent.startDate > newEvent.endDate) {
newEvent.endDate = newEvent.startDate;
}
if (newEvent.title.length > 100) {
newEvent.title = newEvent.title.substring(0, 100);
}
return newEvent;
}, {
title: '',
description: '',
startDate: null,
endDate: null,
});
Congratulations.
You’ve replaced readable logic with a pseudo–Redux wormhole to feel more "correct."
💡 Why Coupling Validation with State Stinks
You're baking validation logic into the state update mechanism. That means:
- Every state change must trigger validation — even when it shouldn't
- You can’t test validation separately
- You can’t control when or how validation happens
- You’ve locked logic into an opaque reducer blob
Validation is not a reducer's job. A reducer should handle transitions, not rules.
✅ The Clean Way (That Just Works)
Let’s write boring, effective, adult code:
function useForm(initialValues) {
const [title, setTitle] = useState(initialValues.title);
const [description, setDescription] = useState(initialValues.description);
const [startDate, setStartDate] = useState(initialValues.startDate);
const [endDate, setEndDate] = useState(initialValues.endDate);
return {
title,
setTitle,
description,
setDescription,
startDate,
setStartDate,
endDate,
setEndDate,
};
}
const {
setTitle,
setDescription,
setStartDate,
setEndDate,
...event,
} = useForm({
title: '',
description: '',
startDate: null,
endDate: null,
});
const validateEvent = (event) => {
const errors = {};
if (event.startDate > event.endDate) {
errors.date = 'End date must be after start date';
}
if (event.title.length > 100) {
errors.title = 'Title must be under 100 characters';
}
return errors;
};
You can now:
- Validate on blur
- Show errors inline
- Test validateEvent independently
- Reuse the same validation in the backend
- Extend logic without digging through reducer guts
This is code that’s actually maintainable.
🤡 The Real Reason People Push useReducer
You are probably an influencers trying to sell your "How to be a 10x dev? I know, you don't" course when your only real skill is clearing interviews.
You exploit the insecurities of innocent junior devs without providing them any real value in return and by making them feel:
- insecure about writing “basic” code.
-
useState
looks amateur - Abstracting everything into a reducer makes it “professional”
- More indirection = better architecture
It’s the same mindset that leads to:
- Overuse of design patterns
- Creating types.ts files for three enums
- Wrapping simple logic in ten layers of abstraction
You're not a worse dev for writing useState
.
You’re a better one for knowing when not to reach for complexity.
🧠 When useReducer Actually Belongs
Let’s be fair. Here’s when it makes sense:
✅ Finite State Machines (e.g., multi-step wizards)
✅ Undo/Redo logic
✅ Pure state transitions you want to extract + test
✅ Shared reducer logic across multiple components
✅ You're modeling explicit transitions, not just storing values
If that’s not you, and you’re still using useReducer, ask yourself:
Am I solving a real problem?
Or just inventing stuff to feel clever?
✍️ The Rule of Thumb
useState
is for values.
Custom hooks are for reuse.
useReducer
is for transitions.
Write code that’s easy to read, not code that performs intellectual gymnastics.
Break this rule of thumb if it gets in the way.
🔥 Final Thought
You don’t get a promotion for writing dispatch()
.
You don’t become a better engineer by mimicking Redux on your delete confirmation modal.
You become a better engineer by writing clear, boring, durable code your teammates can understand without context.
So unless your state has explicit transitions, interdependencies, or reversibility — just use useState
.
Don't be a sheep 🐑.
You almost never need useReducer
.
Hey blockchain user! snag your instant $50 worth of crypto in DuckyBSC rewards quick! — Claim your tokens! Web3 wallet required to access airdrop. 👉 duckybsc.xyz