State Management & Data Fetching
Roughly 18% of a typical loop — and 18% of the mock exam here.
Where state lives: lift it, then stop
The opening question in most loops: two sibling components need the same data. The answer is lifting state — move it to the closest common parent, pass it down as props, pass changes back up as callbacks. The trap: reaching for Context or a store the moment prop-passing feels tedious. The react.dev ordering is explicit: pass props first; if intermediate layers only forward data, extract components and pass JSX as children instead; only when both fail, consider Context.
Context and its blast radius
Context fits low-frequency, wide-reach values — theming, the current user, routing, reducer-backed app state. It fails as a general store because of the re-render blast radius:
- When a provider’s
valuechanges (compared byObject.is), React re-renders every consumer beneath it. - Wrapping a consumer in
memodoes not help — memoized children still receive fresh context values. - An inline object literal as the provider value mints a new reference every render — memoize it with
useMemoanduseCallback.
Context plus useReducer is the sanctioned scaling step: the reducer centralizes transitions, one provider carries state, a second carries dispatch — which is referentially stable, so components that only trigger updates never re-render on state changes. Two React 19 notes: render <ThemeContext value> directly (no .Provider), and use(ThemeContext) works inside conditionals, unlike useContext.
External stores: Redux Toolkit and Zustand
Redux Toolkit’s createSlice generates action creators and action types from your case reducers; inside them you write “mutating” code safely because Immer tracks draft mutations and emits immutable updates. RTK earns its weight when many features share state and you want middleware, devtools time travel, or a serializable action log — not for a modal flag and a form.
Zustand inverts the model: the store lives outside the component tree, create returns a hook, and components subscribe through selectors — useStore((s) => s.count) — re-rendering only when the selected slice changes. No provider, minimal boilerplate. Selector-based subscription is the direct answer to the blast-radius problem above.
Server state is not client state
Server data is a cache of someone else’s truth: async, shared, stale the moment it arrives. Copying fetch results into Redux or useState hand-rolls caching, dedupe, and invalidation — the trap answer. TanStack Query owns that lifecycle around a query key and a fetch function:
staleTimedefaults to zero — cached data is stale immediately, and stale queries refetch in the background on mount, window refocus, and network reconnect; raise it to declare how long data stays fresh.gcTime(default five minutes) controls when inactive cache entries are garbage-collected.- Failed queries retry 3 times with exponential backoff before surfacing an error.
- After a mutation,
invalidateQuerieswith a key prefix marks matching queries stale and refetches the active ones.
Name the model: stale-while-revalidate — render cached data instantly, update it quietly.
Derived state, forms, and the URL
The highest-frequency trap: state you can compute. If a value derives from existing props or state, calculate it during render — never mirror it into state via an effect:
// redundant: extra render pass, sync bugs
const [fullName, setFullName] = useState('');
useEffect(() => { setFullName(first + ' ' + last); }, [first, last]);
// derive it during render instead
const fullName = first + ' ' + last;
Expensive derivations get useMemo, not state plus an effect; resetting a subtree when an identity changes is a key change, not a sync effect.
Forms: controlled inputs (value plus onChange) when the UI reacts per keystroke; uncontrolled inputs read at submit when it does not. Form libraries win here by subscribing to individual fields, avoiding whole-form re-renders on every keystroke.
URL as state is the differentiator answer: filters, pagination, tabs, and selected items belong in search params — shareable, bookmarkable, refresh-proof, with back-button behavior for free. Rule of thumb: if a user might share a link to the view, the state belongs in the URL.
Sample questions
6 of the 36 questions this domain carries in practice mode — expand one to check yourself before drilling.
1. A search box component and a results list component are siblings, and the list needs the current query. The idiomatic React fix is:
- store the query on window so both components can read it during render
- keep a query state in each sibling and synchronize them with a useEffect in both directions
- move the query state up to their closest common parent and pass it down as props
- give both components the same key so React shares their state between them
Answer: C. Lifting state to the nearest common ancestor keeps one source of truth; react.dev calls two-way effect syncing a common source of subtle bugs.
2. In React terms, "lifting state up" means:
- moving shared state to the closest common ancestor so one component owns the single source of truth
- moving state into a child component so the parent re-renders less often
- copying the state into a shared module-level variable so any file in the project can import and update it
- promoting a useState call to useReducer once it holds an object
Answer: A. The owner passes the value and update handlers down as props, so children stay controlled and cannot drift out of sync with each other.
3. After lifting state, a value now threads as a prop through five intermediate components that never read it. Before reaching for Context, react.dev suggests:
- converting the intermediate components to PureComponent so the pass-through costs nothing
- storing the value in a ref, since refs travel through the tree without re-rendering
- merging the five components into one so there is nothing to thread through
- restructuring with composition, passing the rendered child down as children or a JSX prop
Answer: D. When layout components accept children, the data owner renders the consumer directly, so intermediates never see the prop and no context is needed at all.
4. A provider publishes the current textarea draft through context so a distant preview pane can read it, and now typing lags across the whole section. The core problem is:
- every consumer re-renders on each keystroke, because context cannot subscribe to a slice
- context values are diffed deeply, which is slow for long strings
- the textarea should be uncontrolled, since controlled inputs cannot publish their value into context
- the provider batches keystrokes, so consumers render twice per character
Answer: A. useContext offers no selector, so high-frequency values belong in local state, or in an external store where components subscribe to just the slice they render.
5. A component wrapped in memo re-renders even though logging shows its props are identical between renders. Which explanation fits?
- memo compares props deeply, and deep comparison always fails on functions
- it reads a context whose value changed, and context updates bypass memo's props check
- its parent rendered it through cloneElement, which always defeats memo
- memo skips its comparison entirely in development builds, so this is just StrictMode double-render noise
Answer: B. memo only gates renders caused by the parent; useContext, useState and store subscriptions inside the component all schedule renders no props comparison can stop.
6. A refetch returns byte-identical JSON, yet the team expects every useMemo depending on the query data to recompute. Nothing recomputes, because TanStack Query:
- skips writing to the cache whenever the HTTP status is 304
- defers writing results into the cache until some component next reads the query data again
- structurally shares results, keeping the previous references when values are unchanged
- freezes query data, so memo hooks treat all of it as constant
Answer: C. Structural sharing preserves reference identity down to unchanged branches of JSON-compatible data, keeping useMemo, useEffect and memo children calm across identical refetches.
Drill this domain in practice mode →
Independent community study resource — not affiliated with or endorsed by Meta Platforms, Inc. React is a trademark of Meta Platforms, Inc. All questions and study notes are original, written from the official React documentation. Everything runs in your browser; nothing you answer is stored or transmitted.