HuntExams Academy logo
ReactAdvanced#state#patterns

How do you share state between distant components?

Answer

Options in order of weight: lift state to a common parent, use context for slow-changing global data, use a query cache for server state, and use a store like Zustand or Redux for large shared client state.

Example

// server state
const { data } = useQuery({ queryKey: ['user'], queryFn: getUser });
// client state
const theme = useContext(ThemeCtx);

Detailed explanation

Sharing state between distant React components means giving them access to the same source of truth without maintaining duplicate values that can drift apart. The right solution depends on who owns the data, which components need it, how often it changes, and whether it comes from a server. Start with the smallest suitable scope rather than making everything global. Interviewers look for ownership and rendering trade-offs, not just a list of libraries.

First, lift state to the nearest common ancestor and pass values and event handlers through props. This keeps data flow explicit and works well when only a few components participate. If intermediate components merely forward props, composition can sometimes remove that plumbing. For widely needed client values such as a theme or locale, Context lets descendants read the nearest provider's value with useContext(ThemeCtx). Context distributes a value; useState or useReducer usually manages its updates. When the provider value changes according to Object.is, React updates consumers of that context, even if they use only one property. Splitting contexts and stabilising object values can reduce unnecessary updates, but memoisation does not provide field-level subscriptions.

For server-owned data, prefer a query cache such as TanStack Query rather than copying fetched results into Context or a general-purpose store. For example, useQuery({ queryKey: ['user'], queryFn: getUser }) connects components to a shared cache entry within the same query client. The cache manages fetching, freshness, request deduplication, and invalidation. Query keys must include relevant inputs, such as a user ID, so unrelated resources do not accidentally share an entry.

For complex shared client state, such as a multi-panel editor or a cross-route draft, consider Zustand or Redux Toolkit. These stores offer subscription mechanisms and selectors so components can consume specific slices, although update behaviour depends on selector results and equality checks. Redux Toolkit also provides structured update patterns and debugging support. Keep temporary UI state local whenever possible. A strong answer presents these as choices by responsibility, not a rigid escalation ladder: one application can sensibly use local state, Context, a query cache, and a client store together.

Key points to remember

  • Lift state to the nearest common ancestor when explicit props remain manageable.
  • Use Context for broadly needed values, especially slow-changing configuration.
  • Use a query cache for server state, including freshness, refetching, and invalidation.
  • Use Zustand or Redux Toolkit for complex shared client state with selective subscriptions.
  • Avoid duplicate sources of truth and keep transient UI state local.

Common mistakes candidates make

  • Putting all state into one Context and assuming consumers subscribe only to the properties they read.
  • Copying query results into a client store without a clear ownership or synchronisation requirement.
  • Introducing a global store before checking whether lifting state or composition is sufficient.

Likely follow-up questions

Does React.memo prevent re-renders caused by Context updates?

No: a component reading a context can re-render when that context changes even if its props are unchanged. Split contexts by responsibility or use a selector-based store when consumers need independent subscriptions.

How do distant components share server data with TanStack Query?

Components using the same query key under the same QueryClient subscribe to the same cached resource. Include all inputs that identify the resource in the key, and invalidate relevant queries after mutations when appropriate.

When would you choose Redux Toolkit over Context?

Redux Toolkit is useful when shared client state has complex transitions, many consumers, or a need for traceable updates and debugging. Context with useReducer can still suit smaller cases, but it does not itself provide selector-based subscriptions.

Interview tip: Explain state ownership first, then compare the options using one concrete example and their rendering trade-offs.

Related Interview Questions

1
ReactAdvanced#state#data

What is the difference between server state and client state?

Open
2
ReactIntermediate#state

Why should you not mutate state directly?

Open
3
ReactIntermediate#routing

How do you handle routing in React?

Open