Skip to the chapter
←ReactGGroundwork⚛React
React/Advanced/A23
56 / 57 written chapters
/

The cover
  1. B1Setup & JSX
  2. B2Thinking in React
  3. B3Components
  4. B4Props
  5. B5useState
  6. B6Events & conditionals
  7. B7Lists & keys
  8. B8Forms
  9. B9useEffect
  10. B10Lifting state & styling
  11. B11Guided project
  12. ★Cheat page
  1. I1useRef
  2. I2Context
  3. I3useReducer
  4. I4Custom hooks
  5. I5Effects in depth
  6. I6Routing
  7. I7State architecture
  8. I8Memoisation
  9. I9Boundaries & portals
  10. I10Data fetching
  11. I11Forms at scale
  12. I12State libraries
  13. I13Auth
  14. I14TanStack Query
  15. I15Testing
  16. I16TypeScript
  17. I17Animation
  18. I18E2E testing
  19. I19i18n
  20. I20File uploads
  21. I21Real-time UI
  22. I22shadcn/ui
  23. ★Cheat page
  1. A1Fiber
  2. A2Concurrent
  3. A3Suspense
  4. A4Server Components
  5. A5Server Actions
  6. A6use()
  7. A7Rendering strategies
  8. A8Effect timing
  9. A9External stores
  10. A10Refs in depth
  11. A11Component patterns
  12. A12Performance
  13. A13Accessibility
  14. A14Design systems
  15. A15Advanced TypeScript
  16. A16Architecture at scale
  17. A17Custom renderers
  18. A18Migrating & upgrading
  19. A19Production debugging
  20. A20Review & judgement
  21. A21State machines
  22. A22Feature flags
  23. A23Interview bank
  24. ★Cheat page

⚛A23 · Part 3 · Advanced

React interview bank

Forty-six questions split by level, and twenty output drills.

20 min read5 sections2 exercises
56 of 57

In this chapter

  • How to use this
  • Fresher — 0 to 2 years
  • Mid — 2 to 5 years
  • Senior — 5 years and up
  • Output drills — say what happens before you run it
about 20 min
05On this page
  1. How to use this
  2. Fresher — 0 to 2 years
  3. Mid — 2 to 5 years
  4. Senior — 5 years and up
  5. Output drills — say what happens before you run it

How to use this

Answer out loud before reading. The React round is mostly follow-ups — the first answer buys you the second question — so rehearse the chain, not the one-liner. Links go to the chapter where the reasoning lives.

Fresher — 0 to 2 years

What is JSX, and what does it compile to?

Syntax sugar for a function call that returns a plain object describing the UI — { type, props }. That is why it is an expression, why you cannot return two sibling tags, and why the rules feel like JavaScript rules rather than HTML ones.

Difference between props and state?

Props come from the parent and are read-only inside the component. State is owned by the component and changing it schedules a re-render. Data flows down as props; changes flow up as callbacks.

Why can't you write if inside JSX?

Braces take an expression, and if is a statement — it produces no value. Hence ternaries, && and .map().

What is the key prop for?

Item identity between renders, so React can match a new element to an existing DOM node. Not a performance hint. With index keys, deleting from the middle makes React preserve state on the wrong row — typed text and checkboxes move.

Why does setCount(count + 1) three times only add one?

Each render is a snapshot; all three read the same count. The updater form setCount(c => c + 1) receives the latest value and adds three.

Controlled versus uncontrolled input?

Controlled renders from state and reports changes back — React is the source of truth. Uncontrolled keeps the value in the DOM and you read it at submit, usually with FormData. Never switch a component between the two mid-life.

Why does {items.length && <List />} print a zero?

0 && x evaluates to 0, and React renders zero as text — unlike false, null and undefined, which render nothing. Force a boolean: length > 0 &&.

What is useEffect for?

Synchronising with something outside React — a subscription, a timer, a browser API. It runs after paint; the returned function cleans up before the next run and on unmount. The interesting half is what is not an effect: derived values, anything caused by a user action, and resetting state on a prop change.

Mid — 2 to 5 years

Explain the rules of hooks. Why do they exist?

Top level only, and only from React functions. React has no names for your hooks — it matches them to stored state by call order. Put one inside an if and every slot after it shifts, so one piece of state returns another's value.

What is a stale closure, and how do you fix it?

An effect captured the values of the render it ran in, and never re-ran — so an interval logs the first count forever. Four fixes in order: the updater form; add the dependency and accept the restart; useEffectEvent, which reads the latest value without depending on it; or a ref. See effects in depth.

What actually happens when state changes?

Render is a pure call producing an element tree, and is interruptible. Commit mutates the DOM and is synchronous. Diffing is per position and per type: same type keeps the node and its state, different type unmounts and remounts.

When does memo do nothing?

When any prop is a new reference each render — an inline arrow, an object or array literal, a style object. memo compares props shallowly, so the check fails every time and you have added cost for nothing. It is a contract with every call site, not one decision.

Context versus a state library?

Context is a transport with no selectors: when the provider value changes by identity, every consumer re-renders, even one reading a field that did not change. A store subscribes per slice. Also memoise the provider value, or an object literal re-renders everyone on every parent render.

When would you use useReducer over useState?

When several fields change together, when the same transition happens from many places, or when you want the logic testable without rendering. The real payoff is that impossible states stop being representable — four booleans give sixteen combinations, one status field gives four.

What is wrong with fetching in useEffect?

Five things: fetch does not throw on a 404, so errors pass silently; a race where a slow earlier response overwrites a newer one; no cache; no deduplication between components; and waterfalls, since each level discovers what it needs only after its parent rendered. See data fetching.

How do you reset a component's state when a prop changes?

<Form key={userId} />. React sees a new key, unmounts and mounts fresh. An effect watching the prop renders once with the stale value first.

Difference between useMemo and useCallback?

useCallback(fn, d) is useMemo(() => fn, d). One caches a value, the other a function identity. Both are for expense or for referential stability, and both cost something — memoising arithmetic loses.

How would you render ten thousand rows?

Virtualise — render the twenty that are visible. The bottleneck is DOM node count, which is the browser's problem, not React's; no amount of memo substitutes for it. Mention the costs: Ctrl-F stops finding off-screen content, and variable heights get awkward.

What is a custom hook, and what does it share?

A function starting with use that calls other hooks. It shares logic, never state — two components calling it get independent state. Shared state is context or a store.

How do you type a component's props?

Annotate the parameter; no React.FC. Extend the DOM element rather than restating attributes: React.ComponentProps<"button"> & { variant?: "primary" | "ghost" }.

Why does a Zustand selector returning { a, b } break?

The store compares the selector's result with the last one by reference, and an object literal is new every call, so it always looks changed. In Zustand 4 that was a re-render on every store update; in Zustand 5 it loops until React throws "Maximum update depth exceeded". Select each value separately, or wrap the selector in useShallow, which compares the object's fields instead. See State libraries.

Why is pending always false in my useFormStatus?

It reads the status of the parent form of the component that calls it, never a form that component renders itself. Calling it next to the <form> tag returns false forever. Move it into a child, such as a <SubmitButton /> rendered inside the form, which is also what makes that button reusable with no props.

What do route loaders give you over fetching in an effect?

The data is there on the first render, so there is no loading branch in the component. Loaders for every matched route run in parallel, which removes the render-fetch waterfall. And the router cancels a superseded navigation, so a slow response for the previous page cannot overwrite the current one. After an action, loaders revalidate without manual refetching. See React Router.

How do you accept a ref in a wrapper component in React 19, with TypeScript?

Type the props as React.ComponentProps<"input"> and spread them onto the input. ref is an ordinary prop since React 19, and that type already includes it, so no forwardRef is needed. Use ComponentPropsWithoutRef when a wrapper must not pass a ref through. See TypeScript with React.

Senior — 5 years and up

What is Fiber, and what did it change?

Reconciliation moved from recursion to a walk over a linked list of work units, so rendering became interruptible while commit stayed synchronous. Everything after it — time slicing, transitions, Suspense, streaming — needed that one capability. It is also why components must be pure: a render may be abandoned and re-run.

How does React skip work?

Bailouts. If props are referentially equal with no pending state, React reuses the fiber without calling your component. If the element object is identical — which is what happens when you pass children straight through — the whole subtree is skipped. That is why composition beats memo.

What are concurrent features actually doing?

Prioritising. startTransition marks an update interruptible, so a keystroke is never queued behind it, and React discards in-progress low-priority work when newer input arrives. Not a debounce — it yields on demand rather than waiting a fixed time. It cannot help a single 300ms synchronous block, because there is no yield point inside one.

Server Components versus SSR?

SSR renders your components on the server for fast HTML, then ships the same components to run again in the browser. Server Components run only on the server and ship no JavaScript — the client receives a serialised element tree. They can await data directly, which removes the API layer and the render-fetch waterfall.

What does "use client" actually mark?

A boundary, not a component. Everything the file imports becomes client code transitively, so it belongs as low in the tree as possible. A client component cannot import a server one, but can receive one as children — by then it is a rendered payload rather than code.

What is the risk with Server Actions?

"use server" publishes a public HTTP endpoint. Being imported by one form protects nothing — anyone can call it with any arguments. Every action authenticates, authorises and validates for itself, exactly like a REST route.

Explain Suspense, and its interaction with transitions.

A component suspends by throwing a promise; the nearest boundary shows its fallback and retries on resolve. The important interaction: inside a transition, React keeps the current content on screen rather than replacing it with the fallback — which is the difference between a page that flashes on every navigation and one that feels continuous.

Why does useSyncExternalStore exist?

Tearing. With the effect-and-state pattern, an external value can change mid-render under concurrency, so two components show different values of the same store in one frame. It also gives a server snapshot, which the naive version cannot. Every state library moved to it in React 18.

How would you debug a slow React page?

Profile first, with "record why each component rendered" on — "slow" means five different things. Then fix structure before caching: move state down, pass children, virtualise, code-split. Memoise last, at the place the profiler pointed. Then measure again and delete what did not help.

How do you find a memory leak in a React app?

Heap snapshot, repeat a navigation five times, snapshot again, compare retained size and sort by detached nodes. The cause is almost always an effect with no cleanup — a listener, an interval, a subscription — holding a closure over something large. Strict Mode's double mount makes it show up twice as fast.

You inherit a 200,000-line class-based app on React 17. What do you do?

Not a rewrite. Get onto a supported version first, then move server state out of Redux into a query cache — usually the biggest single deletion. TypeScript file by file. Convert classes only when you are already in the file. Put the progress numbers in CI, and name the code you will deliberately never touch.

What does the React Compiler do, and should you delete your useMemo calls?

It memoises at build time by analysing which values depend on which, including after an early return where a hook could never go. For new code, rely on it and keep useMemo/useCallback as escape hatches for exact control. For existing code, leave the memoisation in place or remove it only with testing, because removing it can change the compiled output. Adopt it with the eslint-plugin-react-hooks rules first, then annotation mode and "use memo". See Memoisation.

What was React2Shell, and what did it change about how you ship Server Components?

CVE-2025-55182, CVSS 10.0, disclosed December 2025: a crafted request to any Server Function endpoint could run code on the server, with no authentication, and it was exploited within days. Pure client apps were unaffected. The lessons are that every Server Function is a public endpoint that must validate and authorise its input, that the framework has to be upgraded as well as React, and that tracking advisories is part of the job. See Server Components.

How would you animate a route change in React today?

With <ViewTransition>, stable since React 19.3. Wrap the page, trigger the navigation inside startTransition — a plain setState does not animate — and use addTransitionType to pick a different animation for back and forward. Put the boundary before any DOM node, name matching boundaries for a shared-element morph, and respect prefers-reduced-motion. See Animation.

What would you look for reviewing someone's React?

State that should not exist — derived values stored, or the same truth twice. Effects that are not synchronisation. Missing loading, empty and error states. Index keys. Icon buttons with no accessible name. And the question that catches most of it: what happens the second time?

In a Server Component, when do you await and when do you pass a promise to use?

Await data the component itself renders. Pass an unawaited promise when a child further down renders it, so the request starts early without blocking the fast parts of the page, and a Suspense boundary decides what shows meanwhile. On the client, use needs a cached promise — one created during render loops — and it cannot sit inside try/catch; rejection goes to an error boundary, or becomes a value with .catch before it is passed in. See The use() hook.

How do Suspense and transitions decide whether the user sees a fallback?

A boundary showing content goes back to its fallback when something inside suspends, unless the update came from startTransition or useDeferredValue, in which case the old content stays. That default is wrong when the content is genuinely different — another user's profile — so a key resets the boundary. React also reveals suspended content at most once every 300ms, so boundaries that finish close together appear together. See Suspense.

What happens to useSyncExternalStore during a transition?

Store updates cannot be transitions. React calls getSnapshot again just before committing a transition, and if the store changed it restarts the render as a blocking update so the screen shows one version. The consequences: do not suspend on a store value, because it will replace visible content with a fallback; and the server snapshot is also what hydration renders, so client-only values change after load. See useSyncExternalStore.

The codebase uses Recoil. What do you do?

Plan a migration. Meta archived the repository on 1 January 2025, so it receives no fixes for new React versions. Jotai is the usual destination because atoms and derived atoms map almost one to one; move one feature at a time behind the same hooks. And before porting a single atom, ask whether it is server data that belongs in a query cache instead. See State libraries.

How do you make a lookup table fail to compile when a new case is added?

const labels = { ... } satisfies Record<Status, string>. satisfies checks the object against the type without widening it, so a missing status or a stale key is an error while the object keeps its precise type. It is exhaustiveness for data, the way assertNever is for a switch. See Advanced TypeScript.

Next.js gives you updateTag and revalidateTag. When is each right?

updateTag is for read-your-own-writes: callable only in a Server Action, and the next read waits for fresh data, so the user who created a post sees it. revalidateTag(tag, "max") marks data stale and serves the old copy while refreshing, and works from route handlers too, which suits webhooks. The one-argument revalidateTag is deprecated in Next.js 16. See Server Actions.

When would you argue against using React?

A mostly static content site — it costs a runtime and hydration on every page to make three widgets work, and islands ship the same UI with almost no JavaScript. A small embedded widget, where bundle size dominates. And a team that knows something else well, because familiarity beats theoretical fit.

Output drills — say what happens before you run it

Interviewers put a snippet on screen and ask what it logs or renders. Each of these hides one rule. Cover the answer, commit to one out loud, then check. Assume React 19 with createRoot, and no Strict Mode unless the drill says so.

1. The count starts at 0. What is logged, and what renders after one click?

function handleClick() {
  setN(n + 1);
  setN(n + 1);
  console.log(n);
}

Logs 0; renders 1. Both calls read the same snapshot, and n in this render never changes.

2. Starting from 0?

setN(n + 5);
setN((c) => c + 1);

6. Updates queue in order: the first replaces the value with 5, the updater then receives 5.

3. Starting from 0?

setN((c) => c + 1);
setN(42);

42. A plain value replaces whatever the queue had built so far.

4. In what order do these log on the first render?

function Parent() {
  console.log("render parent");
  useEffect(() => console.log("effect parent"));
  return <Child />;
}
function Child() {
  console.log("render child");
  useEffect(() => console.log("effect child"));
}

render parent, render child, effect child, effect parent. Rendering goes top down; effects run after commit from the children up, so a parent's effect can rely on its children being set up.

5. In development with Strict Mode, what logs when this mounts?

useEffect(() => {
  console.log("subscribe");
  return () => console.log("unsubscribe");
}, []);

subscribe, unsubscribe, subscribe. Strict Mode mounts, unmounts and remounts once to prove the cleanup works. Production logs subscribe once.

6. Which logs first?

useEffect(() => console.log("effect"));
useLayoutEffect(() => console.log("layout"));

layout, then effect. Layout effects run after the DOM changes but before the browser paints; ordinary effects run after paint. Order in the source does not matter.

7. The user clicks the counter up to 3, then isAdmin flips. What does the counter show?

{isAdmin ? <Counter label="Admin" /> : <Counter label="User" />}

3. Same component type at the same position, so React keeps the instance and its state and only updates the label. Add different keys to reset it.

8. The second counter is at 3. showBanner goes from true to false. What does it show?

<div>
  {showBanner && <Banner />}
  <Counter />
</div>

3. false still occupies the first slot, so Counter stays in the second position and keeps its state.

9. What happens as the user types?

function Form() {
  const [text, setText] = useState("");
  function Field() {
    return <input value={text} onChange={(e) => setText(e.target.value)} />;
  }
  return <Field />;
}

The input loses focus after every keystroke. Field is a new function on every render, so React sees a new component type, unmounts the old input and mounts a fresh one. Define components at module scope.

10. Row is wrapped in memo. Does it re-render when the parent does?

<Row item={item} style={{ padding: 8 }} />

Yes, every time. The style object is new on each render, so the shallow prop comparison always fails.

11. The button is clicked three times. What is on screen?

const clicks = useRef(0);
return <button onClick={() => clicks.current++}>{clicks.current}</button>;

0. The ref really is 3, but changing a ref does not schedule a render. The next render for any other reason will show 3.

12. How many times does the component render after the timeout fires?

setTimeout(() => {
  setA(1);
  setB(2);
}, 100);

Once. Since React 18, updates are batched everywhere — timeouts, promises and native handlers included, not only React events.

13. What does the counter show after five seconds?

useEffect(() => {
  const id = setInterval(() => setCount(count + 1), 1000);
  return () => clearInterval(id);
}, []);

1. The interval closed over the first render's count, which is 0, so it sets 1 every second. setCount((c) => c + 1) fixes it.

14. How many times does load run over ten renders, in production?

const [a] = useState(load());
const [b] = useState(() => load());

Ten for the first line, one for the second. load() is an ordinary call made on every render, and its result is thrown away after the first. Passing the function lets React call it only for the initial value.

15. Does the list update?

function add(item) {
  items.push(item);
  setItems(items);
}

No. The array is the same reference, so React compares with Object.is, sees no change and skips the render. setItems([...items, item]) works.

16. In development with Strict Mode, one "added" action is dispatched. How many items appear?

case "added":
  state.items.push(action.item);
  return { ...state };

Two. Strict Mode calls the reducer twice to expose impurity, and both calls push into the same array. Production shows one, which is exactly why the bug survives until someone compares states.

17. Toolbar is wrapped in memo and reads ThemeContext. App re-renders for an unrelated reason. Does Toolbar?

function App() {
  const [theme] = useState("dark");
  return (
    <ThemeContext value={{ theme }}>
      <Toolbar />
    </ThemeContext>
  );
}

Yes. The value is a new object every time App renders, so every consumer re-renders regardless of memo. Wrap the value in useMemo, or pass the string itself.

18. The count is 0. What is logged?

async function handleClick() {
  setN(n + 1);
  await save();
  console.log(n);
}

0. Awaiting does not refresh the closure. The component has re-rendered with 1 by then, but this function belongs to the render where n was 0.

19. How often does this effect run?

const options = { limit: 10 };
useEffect(() => {
  fetchItems(options);
}, [options]);

After every render. options is a new object each time, so the dependency always looks changed. Move it outside the component, depend on options.limit, or create it inside the effect.

20. What happens on the first render?

const size = useSyncExternalStore(
  subscribe,
  () => ({ width: window.innerWidth })
);

React warns "The result of getSnapshot should be cached", then throws "Maximum update depth exceeded". Every call returns a new object, so the snapshot always looks changed. Return window.innerWidth itself.

The four that decide most React rounds

Hooks rules and why they exist. Reconciliation and keys. What is and is not an effect. The server boundary. Everything else is a follow-up to one of those — rehearse them until the follow-up is where you get interesting, not where you run out.

02Practice this chapter

Opens in the editor: write it, run it, and check it against real tests.

  • 8 testsSimulate a batch of setState callsintermediate
  • 6 testsSame slot, same type: state survivesintermediate
All 54 for this level →
A23

Finished reading?

0 of 57 chapters read in this series

→Next stop

  1. A22Previous · A22Feature flags
  2. A23you are here
  3. Next · ★The React cheat pageEverything worth having in front of you, on one page.4 min read

01On this page

5 sections · 0%

  1. 01How to use this
  2. 02Fresher — 0 to 2 years
  3. 03Mid — 2 to 5 years
  4. 04Senior — 5 years and up
  5. 05Output drills — say what happens before you run it

[] chapters · t top