Skip to solution
mediumFrontend

How do you manage global state in a large React application?

539 views
01

Understand the problem

Question presented to candidate: "How would you approach state management for a large React app? Walk me through the decision."

What a strong answer should cover:

  • The reframe that answers the question: separate server state from client state. Most of what people put in a global store is cached server data — it needs fetching, deduplication, invalidation and staleness rules, not storage.
  • Once server state is handled by a query library, the genuinely global client state left over is usually small: theme, auth session, a cart, UI preferences.
  • The ladder: local state → lifted state → composition → Context → a store, adopting each only when the previous one stops working.
  • Context is transport, not a store — no selectors, so every consumer re-renders.
  • What a store adds that Context cannot: selector-based subscriptions, so a component re-renders only for the slice it reads.
  • The realistic options and what distinguishes them: Redux Toolkit (conventions, devtools, large teams), Zustand (minimal, hook-based), Jotai (atomic, bottom-up), and useSyncExternalStore as the primitive they are all built on.
  • URL state is the forgotten category — filters, tabs, and pagination usually belong in the query string, where they are shareable and survive a refresh.
  • Anti-pattern: one giant store holding everything, which makes change frequency the maximum of everything in it.

Clarifying questions expected:

  • "How much of this is server data versus genuinely client-only state?"
  • "How large is the team — do we need enforced conventions and devtools?"
  • "Does any of it belong in the URL?"

Code / implementation expected: Optional. A small store with a selector demonstrates the point that Context cannot.

state managementcontext apireduxglobal state
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for React interviews — assumes Context and hooks. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. This is a judgement question, so it leans on the measured Context behaviour fro

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

A 20-line store with selectors, next to the Context version
Run Playground
import { useSyncExternalStore, createContext, useContext, useState, useMemo, memo } from "react";

// ── A minimal store. This is roughly what Zustand is, minus the ergonomics.
//    The important part is selector-based subscription. ─────────────────────
function createStore(initial) {
  let state = initial;
  const listeners = new Set();
  return {
    getState: () => state,
    setState: (patch) => {
      state = { ...state, ...patch };
      listeners.forEach((l) => l());
    },
    subscribe: (l) => { listeners.add(l); return () => listeners.delete(l); },
  };
}

const store = createStore({ user: "ada", theme: "dark" });

// The selector is what Context cannot do: this component re-renders ONLY when
// the slice it selected actually changes.
function useStore(selector) {
  return useSyncExternalStore(
    store.subscribe,
    () => selector(store.getState()),
    () => selector(store.getState()),
  );
}

const counts = { storeUser: 0, ctxUser: 0 };

const StoreUser = memo(function StoreUser() {
  counts.storeUser++;
  const user = useStore((s) => s.user);       // subscribes to user only
  return <Line label="store + selector" value={user} n={counts.storeUser} />;
});

// ── The Context equivalent, for contrast. ─────────────────────────────────
const Ctx = createContext(null);

const CtxUser = memo(function CtxUser() {
  counts.ctxUser++;
  const { user } = useContext(Ctx);           // no way to subscribe to just user
  return <Line label="context" value={user} n={counts.ctxUser} />;
});

function Line({ label, value, n }) {
  return (
    <p style={{ margin: "4px 0", fontSize: 14 }}>
      <code style={{ display: "inline-block", minWidth: 150 }}>{label}</code>
      user: <strong>{value}</strong> · renders: <strong>{n}</strong>
    </p>
  );
}

export default function App() {
  const [theme, setTheme] = useState("dark");
  const ctxValue = useMemo(() => ({ user: "ada", theme }), [theme]);

  const changeTheme = () => {
    const next = theme === "dark" ? "light" : "dark";
    setTheme(next);
    store.setState({ theme: next });           // change ONLY the theme slice
  };

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 460 }}>
      <StoreUser />
      <Ctx value={ctxValue}><CtxUser /></Ctx>

      <p><button onClick={changeTheme}>change the theme only (now {theme})</button></p>

      <p style={{ color: "#666", fontSize: 13 }}>
        Click repeatedly. Neither component reads the theme. The store-backed one
        stays at 1 render because its selector returned the same user each time;
        the Context one climbs, because Context has no selectors and notifies
        every consumer on any change.
      </p>
    </div>
  );
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 57 of 119 decoded in the React.js track. One more won't hurt.

Back to track