You Don’t Need Redux (Most of the Time)
You Don’t Need Redux (Most of the Time)

Most React apps are not suffering from missing global state management. They are suffering from unclear ownership, unnecessary abstraction, and developers solving imaginary scaling problems.
Most React applications do not become painful because state is hard.
They become painful because nobody knows where state should live anymore.
I have seen teams add Redux to a dashboard with three API calls, two modals, and a settings form, then spend six months debugging selector behavior, stale state, duplicated requests, hydration issues, middleware side effects, and action chains nobody fully understood.
The application was not scaling.
The confusion was.
This happens constantly in frontend teams. Someone hears that “serious apps use Redux.” Another developer wants “better architecture.” Someone else wants “predictable state management.”
So the team installs Redux before the actual complexity even exists.
Now every button click becomes infrastructure.
Every feature becomes ceremony.
And suddenly the frontend feels heavier than the backend.
The worst part is that this usually starts with good intentions.
Nobody wakes up trying to ruin a codebase.
But overengineering rarely looks dangerous at the beginning. It looks organized. Professional. Enterprise-ready.
Until reality shows up.
Most State Does Not Need to Be Global
This is the mistake that creates most Redux abuse.
Developers confuse “shared” with “global.”
Those are not the same thing.
A modal open state is not global state. A search input is not global state. A dropdown selection inside a settings page is not global state. Even most fetched server data is not really frontend state in the traditional sense.
But teams throw everything into Redux anyway because they want consistency.
What they actually create is dependency sprawl.
I once reviewed a React project where opening a sidebar dispatched three actions, updated two reducers, triggered analytics middleware, and caused unrelated components to rerender because selectors were poorly memoized.
For a sidebar.
The original local useState solution had been replaced because someone thought Redux was “cleaner.”
It was not cleaner.
It was farther away.
This is what happens when state ownership becomes unclear. Local concerns stop being local. Components lose autonomy. Tiny UI interactions become distributed system events.
Now debugging requires tracing action flows across the application instead of reading one component.
A lot of frontend exhaustion comes from this exact problem. Developers are not fighting React. They are fighting unnecessary architecture.
Most state belongs close to where it is used.
const [isOpen, setIsOpen] = useState(false);
That is not amateur code.
That is often the correct code.
There are exceptions, obviously. Authentication state, theme configuration, feature flags, websocket coordination, complex workflows, and cross-page synchronization can justify centralized state.
But those should be intentional decisions, not default instincts.
The mistake is not using Redux.
The mistake is using global infrastructure to avoid thinking carefully about ownership.
A good question is simple:
“Who actually needs this state?”
Usually the answer is smaller than developers think.
Redux Often Solves Coordination Problems You Do Not Have Yet
A lot of teams architect for future scale they never reach.
This is one of the most expensive habits in software engineering.
Developers imagine a future where “many components may need this later,” so they centralize everything immediately.
Now the present becomes slower in order to protect against an imaginary future.
I have seen startups with five users running frontend architectures designed like Fortune 500 internal platforms.
Redux. Sagas. Normalized entities. Event-driven reducers. Middleware pipelines. Action factories. Selector layers.
Meanwhile the application barely had enough complexity to justify React itself.
This happens because developers are rewarded socially for sophistication, not restraint.
Simple solutions rarely look impressive in architecture meetings.
Nobody says:
“Amazing work. You avoided unnecessary abstraction.”
But they should.
Because unnecessary abstraction becomes operational cost later.
Every Redux layer introduces cognitive load:
- More files
- More indirection
- More debugging surface
- More naming decisions
- More boilerplate
- More synchronization issues
- More onboarding friction
- More accidental complexity
And eventually, more meetings.
The irony is painful. Teams adopt Redux to improve predictability, then spend months debugging synchronization problems created by Redux itself.
I once saw a bug where server data updated correctly, Redux updated correctly, selectors updated correctly, but the UI still rendered stale information because memoization assumptions were wrong.
The original version used component state and worked fine.
The “scalable architecture” introduced the bug.
This does not mean scaling concerns are fake. Large applications absolutely exist. Some systems genuinely need sophisticated coordination.
But mature engineers understand timing matters.
Solving tomorrow’s problem too early often creates today’s problem immediately.
The better approach is boring:
Start simple. Measure friction honestly. Introduce complexity only when pressure proves it necessary.
Good architecture is not about preparing for every future.
It is about staying understandable under real pressure.
Redux Makes Debugging Harder When Boundaries Are Weak
One of the most dangerous frontend patterns is invisible state flow.
Redux can accidentally encourage this when teams stop thinking about boundaries.
A component dispatches an action.
The action triggers middleware.
Middleware calls an API.
The reducer updates state.
Selectors derive computed data.
Another component rerenders.
A side effect triggers another dispatch.
Now the original UI bug involves six files and three mental models.
This is not hypothetical.
This is normal Redux debugging in badly disciplined systems.
The problem is not Redux itself. The problem is that Redux makes it easy to hide causality behind architecture layers.
Developers stop seeing what changes what.
That destroys debugging speed.
I once joined an incident call where a production checkout flow failed only for specific coupon combinations. The Redux setup was so abstracted that nobody could immediately explain where pricing state was actually computed.
Some calculations lived in selectors. Some lived in reducers. Some lived in middleware. Some lived in components.
The architecture looked “clean.”
The behavior was fragmented.
That distinction matters.
A lot of frontend systems optimize for visual cleanliness instead of operational clarity.
Those are different goals.
Operational clarity means a developer under pressure can answer:
- Where does this value come from?
- What changes it?
- What depends on it?
- What breaks if it changes?
- Which request produced this state?
- Is this client state or server state?
If answering those questions takes twenty minutes, the architecture is already too expensive.
Redux is safest when teams are disciplined about ownership and flow. Without discipline, it becomes a distributed guessing game.
The better approach is reducing invisible movement.
Keep state transitions close to behavior whenever possible.
Make data flow obvious.
Prefer boring clarity over impressive indirection.
Because production incidents do not care how elegant your folder structure looked.
Most Redux Usage Is Actually Server State Management
This is the part many frontend teams still misunderstand.
A huge amount of Redux usage is not really frontend state management at all.
It is server cache management pretending to be application state.
Fetching users. Caching products. Loading dashboards. Storing paginated responses. Managing request status.
That is server state.
And Redux was never particularly great at handling it.
This is why tools like React Query became so popular. They solved the actual problem developers had:
- caching
- refetching
- invalidation
- stale data
- synchronization
- retries
- background refreshes
Redux often forced teams to manually build these behaviors themselves.
Which meant frontend teams accidentally became cache consistency engineers.
That rarely ends well.
I reviewed a dashboard system once where Redux stored every API response manually. Developers wrote custom loading states, cache invalidation rules, retry logic, optimistic updates, and synchronization behavior.
The result was thousands of lines of infrastructure code surrounding relatively simple business logic.
Most of it disappeared after migrating to React Query.
Not because React Query is magic.
Because the team finally separated server state from UI state.
That distinction matters more than the library itself.
This is also why many modern React applications feel dramatically simpler than older Redux-heavy applications.
Developers stopped forcing all state into one conceptual bucket.
Some state belongs locally. Some state belongs remotely. Some state belongs globally.
Those are different problems.
The exact library matters less than recognizing the difference.
Redux Boilerplate Slowly Kills Momentum
Developers underestimate how exhausting ceremony becomes over time.
One reducer does not feel expensive.
Neither does one action.
Neither does one selector.
But software pain compounds gradually.
Then suddenly every feature requires:
- action types
- reducers
- selectors
- dispatch wiring
- middleware handling
- testing mocks
- type synchronization
- state normalization
- serialization awareness
And the frontend starts feeling bureaucratic.
I have seen junior developers afraid to touch Redux systems because changing anything felt dangerous.
Not because the business logic was hard.
Because the architecture amplified fear.
This is where engineering culture quietly degrades.
Teams stop experimenting. Refactors slow down. Features become negotiation. Developers cargo-cult patterns they barely understand.
And eventually people start saying things like:
“Don’t touch that reducer. Weird things happen.”
That sentence is architectural failure.
A healthy system should feel understandable.
This does not mean all abstraction is bad. Boilerplate can sometimes buy consistency. Predictability matters in large organizations.
But many teams adopt enterprise patterns long before they have enterprise problems.
The result is frontend systems optimized for governance instead of delivery.
There is a difference between structure and friction.
Good engineering understands the difference.
Redux Can Hide Bad Component Design
Sometimes Redux is not solving the real problem.
It is masking weak component architecture.
Developers create deeply tangled component trees, prop drilling becomes annoying, and suddenly Redux gets introduced as a rescue tool.
But the real issue was poor composition.
I once worked on a React application where almost every interaction depended on Redux. After investigation, the root problem was that components were gigantic and responsibilities were blurred.
A single page component managed:
- filters
- pagination
- permissions
- analytics
- modal state
- API calls
- rendering logic
- feature flags
- form state
Redux became the escape hatch for bad separation.
But centralizing the state did not actually simplify the design. It just relocated the complexity.
This is common in React teams.
Instead of asking:
“How should this UI be decomposed?”
They ask:
“How do we share this state globally?”
Those are very different questions.
Good component architecture reduces state-sharing pressure naturally.
Smaller boundaries create clearer ownership.
A lot of prop drilling complaints are really component design complaints.
And yes, excessive prop drilling can absolutely become painful. Context APIs exist for a reason. Shared state is sometimes legitimate.
But mature frontend engineering starts with composition before infrastructure.
Otherwise Redux becomes architectural duct tape.
The Tool Is Not the Problem, the Default Thinking Is
Redux is not bad technology.
That conversation is immature.
Redux solved real problems for real teams, especially before the React ecosystem matured. Large-scale coordination is difficult. Predictable state transitions matter. Time-travel debugging was genuinely useful in many environments.
The issue is not Redux itself.
The issue is developers treating Redux like a rite of passage.
A lot of engineers install tools because they want to feel “serious.”
You see this everywhere in software:
- Kubernetes for tiny apps
- Microservices for small teams
- Event sourcing for CRUD systems
- Distributed tracing for low traffic platforms
- CQRS for basic dashboards
- Redux for local UI state
Complexity becomes identity.
That mindset quietly damages engineering judgment.
Because tools stop being solutions and start becoming symbols.
Strong engineers care less about appearing advanced and more about reducing unnecessary operational weight.
That usually means asking uncomfortable questions:
- What problem are we actually solving?
- Is this complexity proportional?
- What maintenance cost are we introducing?
- Who will debug this at 2 AM?
- Does this improve clarity or just architecture aesthetics?
Those questions matter more than framework popularity.
The exact state library is rarely the true issue.
The thinking behind the decision is.
Most Teams Need Better State Discipline, Not Bigger Libraries
A surprising amount of frontend pain disappears when teams establish a few disciplined habits.
Not fancy architecture.
Discipline.
Things like:
- keeping state close to usage
- separating server state from UI state
- avoiding duplicate sources of truth
- making ownership obvious
- reducing hidden couplin
- simplifying data flow
- avoiding premature abstraction
These practices matter more than whether you use Redux, Zustand, Context, Jotai, MobX, or plain React state.
I have seen tiny React applications become impossible to maintain because nobody respected ownership boundaries.
I have also seen large React systems stay surprisingly understandable with minimal global state because the team stayed disciplined about flow.
This is the nuance many architecture discussions miss.
The library cannot compensate for unclear thinking.
No abstraction can permanently save weak engineering habits.
Good frontend systems are usually boring in the best way.
State lives where it makes sense. Data flow is understandable. Ownership is obvious. Debugging paths are short. Developers can reason about behavior quickly.
That simplicity is not accidental.
It is engineered deliberately.
Simpler React Applications Usually Age Better
One of the biggest lies in frontend engineering is that complexity equals scalability.
Often the opposite is true.
Complex systems age badly because every abstraction becomes another maintenance dependency.
The React applications that survive years of growth usually share similar characteristics:
- fewer hidden behaviors
- fewer synchronization layers
- fewer unnecessary abstractions
- clearer ownership
- predictable data flow
- understandable debugging paths
Not because the developers were less skilled.
Because they were disciplined enough to avoid solving imaginary problems.
This is something many engineers only learn after enough painful rewrites.
A system does not become maintainable because it looks architecturally sophisticated.
It becomes maintainable because exhausted humans can still understand it under pressure.
That is the real test.
Not conference talks. Not GitHub stars. Not folder structure screenshots.
Reality.
And reality is brutally expensive to unclear systems.
Conclusion
Most React applications do not need Redux.
They need clearer ownership.
They need better boundaries.
They need less architectural insecurity.
Redux is not the enemy. Blind complexity is.
There are absolutely applications where Redux makes sense. Large coordination-heavy systems exist. Mature teams sometimes need strong centralized state patterns.
But most frontend pain does not come from missing infrastructure.
It comes from unnecessary infrastructure hiding weak decisions.
Good frontend engineering is not about making the architecture look advanced.
It is about making the system understandable when production pressure arrives.
Because impressive code is easy.
Code that survives reality is hard.
If this felt painfully familiar, that probably means you have already worked inside a system where complexity arrived long before the actual scale did.

메타데이터
- post_id
- dbf0afae962e
- slug
- you-dont-need-redux-most-of-the-time-dbf0afae962e
- url
- https://medium.com/skillstuff/you-dont-need-redux-most-of-the-time-dbf0afae962e
- canonical_url
- https://medium.com/skillstuff/you-dont-need-redux-most-of-the-time-dbf0afae962e
- author_url
- https://medium.com/@muhammadshakir4152
- status
- ok
- fetched_at
- 2026-06-26 03:39:16