Part 6.1 — Understanding State Boundaries: Local, Shared, and Global
State management problems rarely start with Redux.
They start with unclear boundaries.
If you don’t decide where state should live, you’ll feel constant friction: props drilled too deep, contexts re-rendering everything, and “global” state used as a dumping ground. This post gives you a decision framework you’ll reuse on every React project.
1. The Core Question You Must Answer
Before choosing any tool, ask:
“Who needs this state, and how often does it change?”
Everything else flows from that.
State placement is not about libraries.
It’s about scope.
2. Local State: The Default (And Often the Right Answer)
Local state belongs to one component.
Examples:
input values
dropdown open/close
modal visibility
hover state
UI toggles
function SearchBox() {
const [query, setQuery] = React.useState('');
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
);
}Rules of thumb:
if only one component cares → local state
if lifting feels unnecessary → keep it local
Most state should stay here.
3. When Local State Becomes a Smell
Local state becomes problematic when:
sibling components need it
parent needs to coordinate behavior
state must survive unmounts
prop drilling grows deeper than 2–3 levels
At that point, you’re crossing a boundary.
4. Shared State: Lift, Don’t Globalize
Shared state is owned by the closest common parent.
Example:
tabs controlling content
filters affecting a list and a toolbar
pagination controls + results
function UsersPage() {
const [filter, setFilter] = React.useState('');
return (
<>
<UserFilter value={filter} onChange={setFilter} />
<UserList filter={filter} />
</>
);
}This is not “prop drilling”.
This is explicit data flow.
Shared state is healthy when:
ownership is clear
data flows top-down
changes are predictable
5. Don’t Skip the “Lift State Up” Step
Many teams jump straight from local state to Context or Redux.
That’s a mistake.
Ask first:
can I lift this state one level up?
can I pass it explicitly?
If yes, do that.
It’s:
simpler
more debuggable
easier to refactor
6. Global State: Powerful and Dangerous
Global state is state that:
many distant components need
spans multiple routes
must persist across navigation
Examples:
authenticated user
theme (dark/light)
locale
feature flags
These are cross-cutting concerns.
Global state should be:
limited
stable
well-defined
If everything becomes global, nothing is.
7. Signs You’re Overusing Global State
Watch for these red flags:
global store holds form inputs
global store holds temporary UI flags
reducers with dozens of unrelated fields
components reading global state “just in case”
Global state should feel boring—not busy.
8. UI State vs Server State (Critical Distinction)
Not all state is equal.
UI state
loading flags
selected items
modal visibility
Server state
users
posts
paginated results
Server state comes from REST APIs and has:
loading
error
caching
invalidation
Treating server data like local UI state leads to bugs.
We’ll formalize this distinction later in Part 6.6.
9. State Lifetime Matters
Ask:
does this state reset on navigation?
should it survive refresh?
should it persist across sessions?
Examples:
form draft → maybe local
auth session → global
onboarding progress → persisted global
Lifetime influences placement.
10. Decision Matrix (Use This)
Use this mental table:
One component → local state
Few related components → lifted/shared state
Many distant components → Context or Redux
Comes from API → server state (not UI state)
If unsure, start smaller.
It’s easier to promote state than to unwind global sprawl.
11. Why This Matters for Performance
Incorrect boundaries cause:
unnecessary re-renders
large Context updates
complex memoization hacks
Correct boundaries:
minimize re-render scope
keep updates cheap
reduce mental overhead
Performance follows architecture.
12. Architecture Before Tools
Context, reducers, Redux—these are tools.
Boundaries are architecture.
If boundaries are wrong:
tools won’t save you
If boundaries are right:tools become almost trivial
This is why this Part starts here.
13. Summary
You now know how to:
identify local vs shared vs global state
avoid premature global state
lift state deliberately
recognize over-globalization
separate UI state from server state
make state placement decisions confidently
Related
Part 6.6 — Async State, Side Effects & Server State Boundaries
Most React state bugs come from mixing UI state with server state. This post teaches you how to draw a hard line between them—and why that line matters.
Part 6.5 — Introducing Redux Toolkit the Right Way
Redux isn’t the default anymore—but when your app needs it, Redux Toolkit is the cleanest, safest way to introduce global state at scale.
Part 6.4 — Combining Context + useReducer for App-Level State
Context provides access. Reducers provide structure. Together, they form a powerful, lightweight architecture for managing app-level state in React.
Comments