State Management
- React
Deciding where state lives and how it is shared as an app grows — from lifting state up, to context, to dedicated libraries — and keeping server data separate.
Prerequisites
- Component state and the Context API (see Related Topics)
What you'll understand
- What state management actually decides
- The progression from local state to shared state to libraries
- Why server data is a different kind of state
Explanation
State management is the broader question of where each piece of state should live and how components that need it get access. Individual component state (see Related Topics) answers this for one component; state management answers it for an application, where the same data is often needed by several components in different places.
There is a natural progression. Start with local state in the component that owns it. When siblings need to share it, lift the state up to their nearest common parent and pass it down. When a value is needed widely across the tree, distribute it with the Context API. Only when application state becomes large and complex do dedicated libraries (such as Redux or Zustand) earn their place, providing a central store and disciplined update patterns. Reach for each step only when the previous one stops being enough.
A key distinction that saves a lot of pain: server state is not the same as client state. Data fetched from an API (see Related Topics) is a cache of something owned elsewhere; it can go stale, needs refetching, and has loading and error states. Client state, like whether a modal is open, is owned entirely by the app. Managing the two with the same tools is a common mistake; server data is often better handled by a data-fetching library built for it.
Examples
Lifting state up: a shared value moves to the common parent and flows down as props:
function Parent() {
const [query, setQuery] = useState('');
return (
<>
<SearchInput value={query} onChange={setQuery} />
<Results query={query} />
</>
);
}Common mistakes
- Reaching for a global store when local or lifted state would do.
- Duplicating the same source of truth in several places that then drift apart.
- Treating fetched server data as if it were plain client state.
- Putting everything in one enormous global state object.
Best practices
- Keep state as local as possible; lift it only when it must be shared.
- Use context for widely shared values before reaching for a library.
- Adopt a state library only when complexity genuinely warrants it.
- Handle server data with tools designed for caching and refetching (see Related Topics).
Further reading
- React, Managing state — https://react.dev/learn/managing-state
- React, Scaling up with reducer and context — https://react.dev/learn/scaling-up-with-reducer-and-context