[{"data":1,"prerenderedAt":413},["ShallowReactive",2],{"topic-react-rendering-and-performance":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Freact.yml","React interview questions on hooks, components, state and rendering — the most-requested front-end framework in technical interviews.","yml","react",{},"React",2,"frameworks\u002Freact",1,"RA6wemU0xFHrA1ZKCABP1J7MireA3Bt_FCJMOuMgsm0",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Freact-rendering-and-performance.yml","Virtual DOM, reconciliation, React.memo, useMemo\u002FuseCallback patterns, code splitting with lazy\u002FSuspense, and concurrent rendering — how React decides what to paint and how to keep it fast.",{},"Rendering and Performance",4,"rendering-and-performance","topics\u002Freact-rendering-and-performance","H_zu-BFOS71-d8RD4PVRzBWoNCiCBIkvGO4jfkyMFIw",[25,111,184,261,337],{"id":26,"title":27,"body":28,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":36,"navigation":38,"order":13,"path":39,"questions":40,"questionsCount":103,"related":104,"seo":105,"seoDescription":106,"stem":107,"subtopic":108,"topic":19,"topicSlug":21,"updated":109,"__hash__":110},"qa\u002Freact\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation.md","Virtual Dom Reconciliation",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":11,"depth":11,"links":33},"",[],"medium","md",{"subtopicSlug":37},"virtual-dom-reconciliation",true,"\u002Freact\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation",[41,46,50,54,58,62,66,70,75,79,83,87,91,95,99],{"id":42,"difficulty":43,"q":44,"a":45},"what-is-virtual-dom","easy","What is the virtual DOM and why does React use it?","The **virtual DOM** is an in-memory JavaScript object tree that mirrors the\nreal DOM structure. React keeps this lightweight copy and uses it as a\nscratchpad to figure out the minimum set of real DOM mutations needed to\nbring the UI up to date.\n\n```jsx\n\u002F\u002F After setState React builds a new VDOM tree like this in memory:\n{ type: 'ul', props: { className: 'list' }, children: [\n  { type: 'li', props: { key: 'a' }, children: ['Alpha'] },\n  { type: 'li', props: { key: 'b' }, children: ['Beta'] },\n]}\n\u002F\u002F Diffing against the previous tree finds only the changed nodes,\n\u002F\u002F then a single batched DOM update is issued.\n```\n\nDirect DOM manipulation is expensive because the browser must recalculate\nlayout, style, and paint. By diffing two cheap JS objects first, React\nminimises the number of DOM operations — especially important when many\nstate updates happen in quick succession.\n\n**Rule of thumb:** The virtual DOM is a performance optimisation, not a\nfundamental requirement of React — React Native and React Three Fiber use\nthe same reconciler with no DOM at all.\n",{"id":47,"difficulty":43,"q":48,"a":49},"reconciliation-definition","What is reconciliation?","**Reconciliation** is the process React uses to decide how to update the\nreal DOM (or host environment) when state or props change. It compares the\nnewly rendered element tree against the previous one and computes a minimal\ndiff.\n\nReact's reconciler applies two heuristics to keep the diff O(n) instead of\nO(n³):\n\n1. **Different type → tear down.** If the root element of a subtree changes\n   type (e.g., `\u003Cdiv>` → `\u003Cspan>`), React destroys the old subtree and\n   mounts a fresh one.\n2. **Same type → update in place.** If the type is the same, React updates\n   only the changed props and recurses into children.\n\n```jsx\n\u002F\u002F Same type → React patches props, does NOT unmount\n\u003CButton color=\"red\" \u002F>  \u002F\u002F prev\n\u003CButton color=\"blue\" \u002F> \u002F\u002F next  → only color prop updated\n\n\u002F\u002F Different type → full unmount + remount\n\u003Cdiv>Hello\u003C\u002Fdiv>  \u002F\u002F prev\n\u003Cspan>Hello\u003C\u002Fspan> \u002F\u002F next → div destroyed, span created\n```\n\n**Rule of thumb:** Reconciliation is cheap for most apps, but mismatched\nkeys or unstable component types (functions defined in render) can force\nunnecessary unmounts.\n",{"id":51,"difficulty":34,"q":52,"a":53},"fiber-architecture","What is React Fiber and how does it improve on the old stack reconciler?","**Fiber** (React 16+) is a rewrite of React's reconciler that replaces the\nold synchronous, recursive \"stack reconciler\" with an interruptible,\nincremental system.\n\nThe old reconciler walked the component tree in one synchronous call that\ncould not be paused — long trees blocked the main thread and dropped frames.\n\nFiber represents each unit of work as a lightweight **fiber node** (a plain\nJS object) linked in a tree. The reconciler can:\n\n- **Pause** work mid-tree and resume it later.\n- **Abort** lower-priority work when something more urgent arrives.\n- **Reuse** completed work across multiple renders.\n\n```\nFiber node (simplified):\n{\n  type,          \u002F\u002F component type or host string\n  stateNode,     \u002F\u002F real DOM node or class instance\n  child,         \u002F\u002F first child fiber\n  sibling,       \u002F\u002F next sibling fiber\n  return,        \u002F\u002F parent fiber\n  pendingProps,\n  memoizedProps,\n  memoizedState,\n  effectTag,     \u002F\u002F INSERT \u002F UPDATE \u002F DELETE\n}\n```\n\nThis architecture unlocks concurrent features like `startTransition`,\n`useDeferredValue`, and `Suspense` — things that are impossible without the\nability to pause and reprioritise work.\n\n**Rule of thumb:** You rarely interact with Fiber directly, but understanding\nit explains why concurrent React can keep the UI responsive while doing heavy\nrendering work.\n",{"id":55,"difficulty":34,"q":56,"a":57},"diffing-algorithm-keys","Why do lists need keys, and what happens when keys are missing or wrong?","When React reconciles a list of children it needs a way to match old items\nto new items across renders. Without a stable identity, it falls back to\n**index-based matching**, which breaks as soon as items are reordered,\ninserted, or removed.\n\n```jsx\n\u002F\u002F Bad: using index as key\n{items.map((item, i) => \u003CRow key={i} data={item} \u002F>)}\n\u002F\u002F If item at index 0 is deleted, all subsequent rows get new data\n\u002F\u002F but keep their existing DOM nodes and state — visual corruption.\n\n\u002F\u002F Good: stable domain ID\n{items.map(item => \u003CRow key={item.id} data={item} \u002F>)}\n\u002F\u002F React tracks each Row by id; deletion only unmounts one node.\n```\n\nWrong keys cause two classes of bugs:\n\n1. **State leaking across items** — a text input retains its value when the\n   item it belongs to changes.\n2. **Missed unmounts** — components that should destroy (and clean up\n   effects) are reused instead.\n\nKeys also force a fresh mount when you *want* to reset state — give a\ncomponent a different `key` and React treats it as a completely new element.\n\n**Rule of thumb:** Keys must be stable, unique among siblings, and derived\nfrom your data — not from array index unless the list is static and never\nreordered.\n",{"id":59,"difficulty":43,"q":60,"a":61},"render-trigger-conditions","What causes a React component to re-render?","A component re-renders when **React decides its output may have changed**.\nThe four triggers are:\n\n1. **State update** — `setState` \u002F setter from `useState` is called with a\n   value that is not referentially equal to the current one.\n2. **Parent re-renders** — by default, when a parent renders, all its\n   children render too (unless bailed out by `React.memo`).\n3. **Context change** — any component subscribed to a context re-renders\n   when the context value changes.\n4. **`forceUpdate`** — class-component escape hatch, rarely used.\n\n```jsx\nfunction Child({ value }) {\n  \u002F\u002F Re-renders whenever Parent re-renders, even if value is the same,\n  \u002F\u002F unless wrapped in React.memo.\n  return \u003Cdiv>{value}\u003C\u002Fdiv>\n}\n```\n\nReact does **not** automatically bail out based on whether props are\nshallowly equal — that optimisation requires `React.memo` or\n`shouldComponentUpdate`.\n\n**Rule of thumb:** Unnecessary re-renders are usually harmless in small\ntrees; profile before optimising, as `React.memo` and memoisation hooks add\ntheir own overhead.\n",{"id":63,"difficulty":34,"q":64,"a":65},"commit-phase-vs-render-phase","What is the difference between the render phase and the commit phase?","React's update cycle has two distinct phases with very different guarantees.\n\n**Render phase** (pure, may be interrupted):\n- React calls your component function (or `render()`) to get a new element\n  tree.\n- Diffs the result against the previous tree.\n- Builds a list of effects to apply.\n- May be paused, aborted, or restarted in Concurrent Mode — so the render\n  phase must be **side-effect free**.\n\n**Commit phase** (synchronous, cannot be interrupted):\n- React applies DOM mutations from the effect list.\n- Runs `useLayoutEffect` callbacks (before the browser paints).\n- Lets the browser paint.\n- Runs `useEffect` callbacks (after the browser paints).\n\n```jsx\n\u002F\u002F Safe in render phase: pure calculation\nconst doubled = value * 2\n\n\u002F\u002F Safe only in commit phase: DOM mutation, network request\nuseEffect(() => {\n  document.title = `Count: ${count}` \u002F\u002F runs after commit\n}, [count])\n```\n\n**Rule of thumb:** Never put side effects directly in your component body —\nthey may run multiple times or be thrown away during concurrent rendering.\n",{"id":67,"difficulty":34,"q":68,"a":69},"reconciler-vs-renderer","What is the difference between the React reconciler and a renderer?","The **reconciler** (`react` package) is framework-agnostic: it builds and\ndiffs the virtual element tree, manages state and effects, and schedules\nwork. It knows nothing about how to actually display anything.\n\nA **renderer** is the bridge between the reconciler and a specific host\nenvironment. It receives instructions (create node, update prop, delete node)\nand executes them:\n\n| Renderer | Host environment |\n|---|---|\n| `react-dom` | Browser DOM |\n| `react-native` | iOS \u002F Android views |\n| `@react-three\u002Ffiber` | WebGL \u002F Three.js |\n| `react-test-renderer` | Plain JS objects (testing) |\n| `react-pdf` | PDF document |\n\n```jsx\n\u002F\u002F react (reconciler) figures out *what* changed\n\u002F\u002F react-dom (renderer) figures out *how* to apply it to the DOM\nimport { createRoot } from 'react-dom\u002Fclient'\ncreateRoot(document.getElementById('root')).render(\u003CApp \u002F>)\n```\n\n**Rule of thumb:** React's portability comes from this separation — you\ncan write the same component logic and target any host environment by\nswapping the renderer.\n",{"id":71,"difficulty":72,"q":73,"a":74},"bailout-conditions","hard","When does React bail out of re-rendering a subtree?","React skips re-rendering a subtree under two conditions:\n\n1. **Same element reference** — if `renderChildren()` returns the same\n   object (by reference, not value) as last time, React skips diffing that\n   subtree entirely. This is the basis of the `children` prop optimisation:\n   elements created in the parent and passed as props are created before the\n   parent re-renders, so their references remain stable.\n\n2. **`React.memo` \u002F `PureComponent` \u002F `shouldComponentUpdate`** — if the\n   component is wrapped and its props are shallowly equal, React skips the\n   render. `React.memo` with a custom comparator lets you define equality\n   yourself.\n\n```jsx\n\u002F\u002F Pattern 1: stable children reference\nfunction Parent() {\n  const [count, setCount] = useState(0)\n  return \u003CLayout>{\u002F* children created here don't re-create on Parent re-render *\u002F}\u003C\u002FLayout>\n}\n\n\u002F\u002F Pattern 2: React.memo\nconst Child = React.memo(function Child({ value }) {\n  return \u003Cdiv>{value}\u003C\u002Fdiv>\n})\n\u002F\u002F Child re-renders only when `value` changes (shallow equal check)\n```\n\nNote: the setter from `useState` and values from refs never change reference\nacross renders, so they never cause unnecessary re-renders in memoised\nchildren.\n\n**Rule of thumb:** Bail-out only skips the *render* call — effects and\ncontext subscriptions are still checked, and the fibre is still visited\nbriefly in the reconciler's tree walk.\n",{"id":76,"difficulty":34,"q":77,"a":78},"what-is-hydration","What is hydration and why can mismatches cause problems?","**Hydration** is the process by which React attaches event listeners and\ntakes over management of server-rendered HTML without re-creating the DOM\nfrom scratch.\n\nDuring hydration React walks the existing DOM and the virtual tree in\nparallel. If they match, React reuses the nodes (fast). If they don't match\n— a **hydration mismatch** — React either logs a warning and patches the DOM,\nor (in development) tears it down and re-renders from scratch.\n\n```jsx\n\u002F\u002F Common cause: rendering different content on server vs client\n\u003Cp>{typeof window !== 'undefined' ? 'Client' : 'Server'}\u003C\u002Fp>\n\u002F\u002F           ^--- different on server vs browser → mismatch warning\n```\n\nMismatches are problematic because:\n- Patches are extra work (doubles DOM writes on affected nodes).\n- Content flickers when the DOM is replaced.\n- Accessibility tools may read the pre-patch content.\n\nFixes: use `suppressHydrationWarning` for intentionally dynamic values\n(timestamps), defer client-only content with `useEffect` + client-only\n`useState`, or use `\u003CClientOnly>` wrapper patterns.\n\n**Rule of thumb:** Render the exact same tree on server and client; treat any\nhydration warning as a correctness bug, not a cosmetic issue.\n",{"id":80,"difficulty":34,"q":81,"a":82},"strict-mode-double-render","Why does React 18 render components twice in Strict Mode?","In development, React **StrictMode** intentionally calls your component\nfunction twice (then discards the first result) to help you discover\nside effects hidden in the render phase.\n\n```jsx\n\u002F\u002F React calls this TWICE in dev\u002FStrictMode:\nfunction Counter() {\n  console.log('render') \u002F\u002F you'll see this twice in the console\n  const [count, setCount] = useState(0)\n  return \u003Cbutton onClick={() => setCount(c => c + 1)}>{count}\u003C\u002Fbutton>\n}\n```\n\nIf your render function is truly pure (no side effects), double-invocation\nis invisible — you get the same result both times. But if render contains a\nside effect (incrementing a ref, logging an analytics event, mutating\nexternal state), you'll see it doubled, revealing the bug.\n\nThis matches the Concurrent Mode contract: the render phase may be discarded\nand restarted at any time, so it must always be side-effect free.\n\n**Rule of thumb:** Strict Mode double-invocation only happens in development\nand is one of the easiest ways to catch render-phase side effects before they\ncause production bugs.\n",{"id":84,"difficulty":72,"q":85,"a":86},"element-vs-component-vs-fiber","What is the difference between a React element, a component, and a fiber?","These three terms are often confused because they're all part of the same\npipeline.\n\n**React element** — a plain JS object describing what to render. Created\nby JSX (`\u003CButton \u002F>` → `React.createElement(Button, null)`). Cheap to\ncreate, immutable, thrown away after reconciliation.\n\n```js\n\u002F\u002F An element is just data:\n{ type: Button, props: { color: 'blue' }, key: null, ref: null }\n```\n\n**Component** — a function (or class) that accepts props and returns\nelements. It's the template; React calls it to produce elements.\n\n**Fiber** — a live node in React's internal work tree, one per element in\nthe rendered tree. Fibers are long-lived; they persist across renders and\ncarry state, effects, and work-scheduling metadata. They are *not* exposed\nto user code.\n\n```\nFlow:\nJSX → createElement → element (data)\n                           ↓ reconciler\n                        fiber (live node in Fiber tree, carries state)\n                           ↓ renderer\n                      real DOM node\n```\n\n**Rule of thumb:** You write components, you receive elements from JSX,\nand React manages fibers internally — mixing these terms up in an interview\nusually signals a shallow understanding of React internals.\n",{"id":88,"difficulty":72,"q":89,"a":90},"concurrent-rendering-basics","What is concurrent rendering and what problem does it solve?","**Concurrent rendering** (React 18+) lets React prepare multiple versions\nof the UI at the same time and interrupt, pause, or abandon in-progress\nrenders when higher-priority work arrives.\n\nThe problem it solves: in synchronous React, any state update — even a\nlow-priority background one — could block the main thread, causing the UI\nto feel janky during large re-renders (typing into a filtered list,\nanimating while data loads).\n\n```jsx\nimport { startTransition } from 'react'\n\n\u002F\u002F Mark filter update as non-urgent — React can pause it\n\u002F\u002F if the user types again before it finishes\nstartTransition(() => {\n  setFilter(e.target.value)\n})\n```\n\nKey tools:\n- `startTransition` \u002F `useTransition` — mark an update as low priority.\n- `useDeferredValue` — defer a derived value to avoid blocking.\n- `Suspense` — declaratively show a fallback while async work is pending.\n\n**Rule of thumb:** Concurrent rendering is opt-in per update, not a\nmode you flip globally. Most updates are still synchronous; you use\n`startTransition` only for genuinely expensive, deferrable work.\n",{"id":92,"difficulty":43,"q":93,"a":94},"why-pure-render","Why must React component render functions be pure?","A **pure function** always produces the same output for the same inputs and\nhas no side effects. React requires render functions to be pure because:\n\n1. **The render phase can be interrupted and restarted** (Concurrent Mode)\n   — side effects in render would fire multiple times or be abandoned\n   mid-execution.\n2. **Strict Mode double-invokes renders** — to detect purity violations in\n   development.\n3. **Server-side rendering** — the render phase runs in Node.js where no\n   DOM, `window`, or browser APIs exist; impure renders that touch these\n   break SSR.\n\n```jsx\n\u002F\u002F Impure render — BAD\nlet calls = 0\nfunction Counter() {\n  calls++          \u002F\u002F side effect: mutates external state on every render\n  return \u003Cp>{calls}\u003C\u002Fp>\n}\n\n\u002F\u002F Pure render — GOOD\nfunction Counter({ count }) {\n  return \u003Cp>{count}\u003C\u002Fp>  \u002F\u002F same props → same output, every time\n}\n```\n\n**Rule of thumb:** If you need to produce a side effect (fetch, log, update\nthe DOM), put it inside `useEffect` or an event handler — never directly in\nthe function body.\n",{"id":96,"difficulty":34,"q":97,"a":98},"passive-effects-layout-effects","What is the difference between useEffect and useLayoutEffect in terms of when they run in the commit phase?","Both hooks run *after* React commits changes to the DOM, but at different\npoints in the browser's rendering pipeline.\n\n**`useLayoutEffect`** fires **synchronously** after DOM mutations but\n**before** the browser paints. Use it when you need to read layout\ninformation (element sizes, scroll position) or make DOM mutations that\nmust be invisible to the user.\n\n**`useEffect`** fires **asynchronously** after the browser has painted. It\ndoesn't block the visual update, making it the right place for network\nrequests, subscriptions, and analytics.\n\n```\nReact commits DOM mutations\n      ↓\nuseLayoutEffect runs (synchronous, before paint)\n      ↓\nBrowser paints\n      ↓\nuseEffect runs (asynchronous, after paint)\n```\n\n```jsx\nuseLayoutEffect(() => {\n  \u002F\u002F Safe to read DOM measurements here — layout is complete but not painted\n  const height = ref.current.getBoundingClientRect().height\n  setHeight(height) \u002F\u002F won't cause visible flash\n})\n```\n\n**Rule of thumb:** Start with `useEffect`; switch to `useLayoutEffect` only\nif you see a visible flash caused by a DOM measurement + state update cycle.\n",{"id":100,"difficulty":34,"q":101,"a":102},"react-profiler","How do you identify performance bottlenecks in a React application?","The primary tool is the **React DevTools Profiler** tab, which records a\nflame chart of which components rendered, how long each took, and why each\nrendered (changed props, state, context, or parent).\n\nSteps:\n\n1. Open DevTools → Profiler → click Record.\n2. Perform the interaction that feels slow.\n3. Stop recording and inspect the flame chart.\n4. Look for components with long render times or high render counts.\n5. Check \"Why did this render?\" for specific cause.\n\n```jsx\n\u002F\u002F Instrument a component boundary for programmatic profiling\n\u003CReact.Profiler id=\"FilterPanel\" onRender={(id, phase, actualDuration) => {\n  console.log(id, phase, actualDuration)\n}}>\n  \u003CFilterPanel \u002F>\n\u003C\u002FReact.Profiler>\n```\n\nAlso useful:\n- Browser Performance tab for the full main-thread timeline.\n- `console.time` \u002F `console.timeEnd` around suspected slow code.\n- `why-did-you-render` library to log unexpected re-renders.\n\n**Rule of thumb:** Profile first, then optimise. Adding `React.memo` and\nmemoisation hooks everywhere without profiling often makes performance *worse*\nby increasing the cost of the equality checks themselves.\n",15,null,{"description":32},"React virtual DOM and reconciliation interview questions — diffing algorithm, Fiber architecture, keys, bailout conditions, and how React decides what to re-render.","react\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation","Virtual DOM and Reconciliation","2026-06-24","Eg1x64GHznlb0XcJS2vShptyDF0E5StXiLMG8z1yzVw",{"id":112,"title":113,"body":114,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":118,"navigation":38,"order":11,"path":120,"questions":121,"questionsCount":178,"related":104,"seo":179,"seoDescription":180,"stem":181,"subtopic":182,"topic":19,"topicSlug":21,"updated":109,"__hash__":183},"qa\u002Freact\u002Frendering-and-performance\u002Freact-memo.md","React Memo",{"type":29,"value":115,"toc":116},[],{"title":32,"searchDepth":11,"depth":11,"links":117},[],{"subtopicSlug":119},"react-memo","\u002Freact\u002Frendering-and-performance\u002Freact-memo",[122,126,130,134,138,142,146,150,154,158,162,166,170,174],{"id":123,"difficulty":43,"q":124,"a":125},"what-is-react-memo","What does React.memo do?","`React.memo` is a **higher-order component** that wraps a function component\nand skips re-rendering it when its props are shallowly equal to the previous\nrender's props.\n\n```jsx\nconst Button = React.memo(function Button({ label, onClick }) {\n  console.log('render')\n  return \u003Cbutton onClick={onClick}>{label}\u003C\u002Fbutton>\n})\n\n\u002F\u002F Parent re-renders with same label and onClick reference:\n\u002F\u002F → Button's console.log does NOT fire\n```\n\nUnder the hood, before calling your component function React compares each\nold prop to each new prop with `Object.is`. If all are equal, it reuses the\nprevious render output.\n\n`React.memo` only applies to function components. The class-component\nequivalent is `PureComponent` (or `shouldComponentUpdate`).\n\n**Rule of thumb:** `React.memo` is a performance hint, not a correctness\nguarantee — React may still re-render the component in some cases (e.g.,\nconcurrent rendering). Never rely on it to prevent side effects from firing.\n",{"id":127,"difficulty":43,"q":128,"a":129},"shallow-equality-explained","What is shallow equality and why does it matter for React.memo?","**Shallow equality** compares values one level deep using `Object.is`:\nprimitives are compared by value; objects and functions are compared by\n**reference**.\n\n```js\nObject.is(1, 1)           \u002F\u002F true  — same primitive\nObject.is('a', 'a')       \u002F\u002F true  — same primitive\nObject.is([], [])         \u002F\u002F false — different object references\nObject.is({ x: 1 }, { x: 1 }) \u002F\u002F false — different object references\n```\n\nThis is why inline objects and functions as props break memoisation:\n\n```jsx\n\u002F\u002F BAD: new object on every render → memo always re-renders\n\u003CChart options={{ color: 'red' }} \u002F>\n\n\u002F\u002F GOOD: stable reference, memo works\nconst OPTIONS = { color: 'red' }\n\u003CChart options={OPTIONS} \u002F>\n\n\u002F\u002F GOOD: memoised object\nconst options = useMemo(() => ({ color }), [color])\n\u003CChart options={options} \u002F>\n```\n\n**Rule of thumb:** Pass primitives when possible; stabilise object and\nfunction props with `useMemo` \u002F `useCallback` when using `React.memo`.\n",{"id":131,"difficulty":34,"q":132,"a":133},"when-to-use-memo","When is React.memo actually worth using?","`React.memo` pays off when **all three** conditions hold:\n\n1. The component renders **frequently** (its parent re-renders often).\n2. The component renders **expensively** (large subtree, heavy computation).\n3. Its **props are stable** (primitives or memoised references).\n\n```jsx\n\u002F\u002F Worth memoising: renders on every keystroke, large list\nconst ResultList = React.memo(function ResultList({ items }) {\n  return (\n    \u003Cul>\n      {items.map(item => \u003Cli key={item.id}>{item.name}\u003C\u002Fli>)}\n    \u003C\u002Ful>\n  )\n})\n\n\u002F\u002F Not worth memoising: renders rarely, trivially cheap\nconst PageTitle = React.memo(function PageTitle({ text }) {\n  return \u003Ch1>{text}\u003C\u002Fh1>\n})\n```\n\nThe overhead of `React.memo` itself (the shallow comparison on every parent\nrender) can exceed the savings if the component is cheap or its props are\nunstable objects.\n\n**Rule of thumb:** Profile first. Reach for `React.memo` only when the\nProfiler shows the component actually re-renders unnecessarily and the\nre-render is measurably expensive.\n",{"id":135,"difficulty":34,"q":136,"a":137},"custom-comparator","How do you use a custom comparator with React.memo?","Pass a second argument to `React.memo`: a function that receives the\n**previous and next props** and returns `true` if the component should\n*skip* re-rendering (i.e., they are \"equal enough\").\n\n```jsx\nconst Chart = React.memo(\n  function Chart({ data, width }) {\n    return \u003Ccanvas ref={drawChart(data, width)} \u002F>\n  },\n  (prevProps, nextProps) => {\n    \u002F\u002F Skip re-render if only the data length changed, not actual values\n    return (\n      prevProps.width === nextProps.width &&\n      prevProps.data.length === nextProps.data.length &&\n      prevProps.data.every((v, i) => v === nextProps.data[i])\n    )\n  }\n)\n```\n\nImportant: returning `true` means **do NOT re-render** (the opposite of\n`shouldComponentUpdate`, which returns `true` to allow a re-render). This\nis a common source of confusion.\n\n**Rule of thumb:** Custom comparators add complexity and can silently\nsuppress needed updates if written incorrectly. Use them only when the\ndefault shallow comparison is measurably too eager.\n",{"id":139,"difficulty":34,"q":140,"a":141},"memo-pitfall-inline-functions","Why do inline function props break React.memo, and how do you fix it?","Each render creates a **new function object**, so the reference changes\nevery time, causing `React.memo`'s shallow comparison to always return\n\"not equal\" and re-render anyway.\n\n```jsx\nfunction Parent() {\n  const [count, setCount] = useState(0)\n\n  \u002F\u002F New function reference on every Parent render → memo useless\n  return \u003CChild onClick={() => console.log('click')} \u002F>\n}\n\nconst Child = React.memo(function Child({ onClick }) {\n  console.log('Child render') \u002F\u002F still fires every time\n  return \u003Cbutton onClick={onClick}>Click\u003C\u002Fbutton>\n})\n```\n\nFix with `useCallback` to stabilise the reference:\n\n```jsx\nfunction Parent() {\n  const [count, setCount] = useState(0)\n\n  const handleClick = useCallback(() => {\n    console.log('click')\n  }, []) \u002F\u002F stable reference — same function object across renders\n\n  return \u003CChild onClick={handleClick} \u002F>\n}\n```\n\n**Rule of thumb:** `React.memo` and `useCallback` are best friends — you\nalmost always need both: memo to skip the child render, and useCallback to\nprevent the function prop from busting the memo.\n",{"id":143,"difficulty":43,"q":144,"a":145},"memo-vs-purecomponent","What is the difference between React.memo and PureComponent?","Both perform a **shallow prop comparison** to avoid unnecessary re-renders,\nbut they apply to different component types.\n\n| | `React.memo` | `PureComponent` |\n|---|---|---|\n| Works with | Function components | Class components |\n| Custom equality | 2nd argument comparator | Override `shouldComponentUpdate` |\n| Checks state | No (state is inside the hook, not a prop) | Yes — shallow-compares both `props` and `state` |\n\n```jsx\n\u002F\u002F Function component\nconst Foo = React.memo(function Foo(props) { … })\n\n\u002F\u002F Class component\nclass Foo extends React.PureComponent {\n  render() { … }\n}\n```\n\n`PureComponent` also shallow-compares `this.state`, which `React.memo`\nhas no equivalent for (state is internal to hooks and React itself handles\nbailing out when the state reference doesn't change).\n\n**Rule of thumb:** In modern React (function components), `React.memo` is the\nstandard tool. `PureComponent` is for legacy class components.\n",{"id":147,"difficulty":72,"q":148,"a":149},"memo-context-caveat","Does React.memo prevent re-renders caused by context changes?","**No.** `React.memo` only skips re-renders caused by changed *props*.\nIf a memoised component subscribes to a context (via `useContext`) and\nthe context value changes, the component **will re-render** regardless\nof whether its props changed.\n\n```jsx\nconst ThemeContext = createContext('light')\n\nconst Card = React.memo(function Card({ title }) {\n  const theme = useContext(ThemeContext) \u002F\u002F subscribed to context\n  return \u003Cdiv className={theme}>{title}\u003C\u002Fdiv>\n})\n\n\u002F\u002F Parent provides a new context value:\n\u002F\u002F Card re-renders even though `title` prop didn't change\n```\n\nMitigation strategies:\n- Split context into multiple smaller contexts so unrelated consumers\n  don't re-render.\n- Use `useMemo` inside the component to memoize the expensive derived\n  output, even if render is called.\n- Use a state-management library (Zustand, Jotai) that uses selectors to\n  subscribe only to the slice of state a component needs.\n\n**Rule of thumb:** `React.memo` and context subscriptions are orthogonal —\nto limit context-driven re-renders you need to narrow the context or use\nselectors, not memo.\n",{"id":151,"difficulty":72,"q":152,"a":153},"memo-children-prop","Why does passing children as a prop often break React.memo?","When you write `\u003CWrapper>\u003CChild \u002F>\u003C\u002FWrapper>`, React creates a new element\nobject for `\u003CChild \u002F>` **on every render of the parent** where the JSX\nlives. Because element objects are new references each time, `React.memo`'s\nshallow comparison sees a changed `children` prop and re-renders.\n\n```jsx\nfunction Parent() {\n  const [count, setCount] = useState(0)\n\n  \u002F\u002F New \u003Cdiv>Hello\u003C\u002Fdiv> element object created every render\n  return \u003CWrapper>{\u003Cdiv>Hello\u003C\u002Fdiv>}\u003C\u002FWrapper>\n}\n\nconst Wrapper = React.memo(function Wrapper({ children }) {\n  console.log('Wrapper render') \u002F\u002F fires on every Parent re-render!\n  return \u003Csection>{children}\u003C\u002Fsection>\n})\n```\n\nFixes:\n1. Move the children JSX outside the re-rendering parent (lift it up).\n2. Wrap children in `useMemo` if they depend on parent state.\n3. Restructure so `Wrapper` doesn't need to be memoised.\n\n**Rule of thumb:** Memoising components that accept `children` is often\ncounter-productive — consider memoising the children themselves instead, or\nrethinking the component hierarchy.\n",{"id":155,"difficulty":34,"q":156,"a":157},"when-not-to-memo","When should you NOT use React.memo?","Avoid `React.memo` when:\n\n1. **Props change on every render anyway** — memo runs the comparison but\n   still re-renders, adding overhead for zero gain.\n2. **The component is trivially cheap** — shallow-comparing many props can\n   cost more than just running the component.\n3. **Props include non-stabilised objects or functions** — memo is\n   immediately defeated and the comparison is wasted work.\n4. **You haven't profiled** — premature memoisation clutters the code and\n   makes it harder to refactor.\n\n```jsx\n\u002F\u002F No point memoising — title is a new string computed in parent every render\nconst Title = React.memo(({ title }) => \u003Ch1>{title}\u003C\u002Fh1>)\n\nfunction Parent({ user }) {\n  return \u003CTitle title={`Hello, ${user.name}!`} \u002F> \u002F\u002F new string each render\n}\n```\n\n**Rule of thumb:** Reach for `React.memo` only after the Profiler confirms\na component re-renders unnecessarily AND that the re-render is expensive.\nDefault to no memoisation.\n",{"id":159,"difficulty":43,"q":160,"a":161},"memo-display-name","How do you preserve the display name of a memoised component for debugging?","When you wrap an anonymous function, React DevTools shows the component as\n`Memo` or `Anonymous`, making the component tree hard to read. Two ways to\npreserve the name:\n\n```jsx\n\u002F\u002F Option 1: named function expression (recommended)\nconst Button = React.memo(function Button({ label }) {\n  return \u003Cbutton>{label}\u003C\u002Fbutton>\n})\n\u002F\u002F DevTools shows: Memo(Button)\n\n\u002F\u002F Option 2: set displayName explicitly\nconst Button = React.memo(({ label }) => \u003Cbutton>{label}\u003C\u002Fbutton>)\nButton.displayName = 'Button'\n\n\u002F\u002F Option 3: extract and export the inner component\nfunction ButtonBase({ label }) { return \u003Cbutton>{label}\u003C\u002Fbutton> }\nexport const Button = React.memo(ButtonBase)\n```\n\n**Rule of thumb:** Always use a named function expression as the first\nargument to `React.memo` — it's the simplest and least error-prone way to\nkeep DevTools readable.\n",{"id":163,"difficulty":34,"q":164,"a":165},"forwardRef-with-memo","How do you combine React.memo with forwardRef?","Wrap with both — the order is `React.memo(React.forwardRef(...))`.\n`forwardRef` should be the inner wrapper because it transforms the\n`(props, ref)` signature; `memo` wraps the result.\n\n```jsx\nimport { forwardRef, memo } from 'react'\n\nconst FancyInput = memo(\n  forwardRef(function FancyInput({ label }, ref) {\n    return (\n      \u003Clabel>\n        {label}\n        \u003Cinput ref={ref} \u002F>\n      \u003C\u002Flabel>\n    )\n  })\n)\n\n\u002F\u002F Usage:\nconst inputRef = useRef()\n\u003CFancyInput ref={inputRef} label=\"Name\" \u002F>\ninputRef.current.focus()\n```\n\nDevTools will show: `Memo(ForwardRef(FancyInput))`.\n\n**Rule of thumb:** Compose in the order `memo(forwardRef(fn))` — memo on\nthe outside handles the prop comparison; forwardRef on the inside handles\nthe ref plumbing.\n",{"id":167,"difficulty":34,"q":168,"a":169},"class-shouldcomponentupdate","How does shouldComponentUpdate relate to React.memo?","`shouldComponentUpdate(nextProps, nextState)` is the class-component\nlifecycle that lets you control whether a component re-renders. Returning\n`false` skips the render; returning `true` allows it.\n\n`React.memo` is the functional-component analogue that handles the same\noptimisation for props only (it has no access to internal state, which React\nmanages automatically).\n\n```jsx\n\u002F\u002F Class component\nclass List extends React.Component {\n  shouldComponentUpdate(nextProps) {\n    return nextProps.items !== this.props.items \u002F\u002F custom check\n  }\n  render() { … }\n}\n\n\u002F\u002F Equivalent with function component + memo\nconst List = React.memo(\n  function List({ items }) { … },\n  (prev, next) => prev.items === next.items \u002F\u002F same semantics, inverted return\n)\n```\n\nKey difference: `shouldComponentUpdate` returning `false` skips the update;\nmemo's comparator returning `true` skips the update (inverted meaning).\n\n**Rule of thumb:** In new code, prefer function components with `React.memo`.\nUse `shouldComponentUpdate` only when maintaining or extending legacy class\ncomponents.\n",{"id":171,"difficulty":34,"q":172,"a":173},"memo-in-lists","When is it beneficial to memoize items inside a large list?","When a list contains many items and the parent state changes frequently\n(e.g., a filter input), each item component gets re-rendered on every\nkeystroke even if its own data hasn't changed. Memoising the item component\nkeeps only the changed item rendering.\n\n```jsx\n\u002F\u002F Without memo: all 1000 items re-render on every keystroke\nfunction ItemRow({ item }) {\n  return \u003Ctr>\u003Ctd>{item.name}\u003C\u002Ftd>\u003Ctd>{item.price}\u003C\u002Ftd>\u003C\u002Ftr>\n}\n\n\u002F\u002F With memo: only items whose props changed re-render\nconst ItemRow = React.memo(function ItemRow({ item }) {\n  return \u003Ctr>\u003Ctd>{item.name}\u003C\u002Ftd>\u003Ctd>{item.price}\u003C\u002Ftd>\u003C\u002Ftr>\n})\n\n\u002F\u002F Parent\nfunction Table({ rows }) {\n  return (\n    \u003Ctbody>\n      {rows.map(row => \u003CItemRow key={row.id} item={row} \u002F>)}\n    \u003C\u002Ftbody>\n  )\n}\n```\n\nFor this to work, `rows` elements must be stable references — if the parent\nrebuilds the array on every render, each `item` prop is a new object and\nmemo is defeated. Stabilise with `useMemo`.\n\n**Rule of thumb:** For large lists (50+ items) where individual items are\npure, `React.memo` on the row component is one of the highest-ROI\noptimisations you can make.\n",{"id":175,"difficulty":72,"q":176,"a":177},"memo-hot-path-cost","What is the cost of React.memo on a hot render path?","On every parent render, React must:\n\n1. Look up the previous props object for the memoised child.\n2. Run `Object.is` on every prop pair (or your custom comparator).\n3. Either skip the child render (if equal) or proceed (if not).\n\nFor a component with many props or complex prop objects, step 2 can be\nnon-trivial — especially if the comparator does deep equality or array\niteration.\n\n```jsx\n\u002F\u002F 20 props, comparator runs 20 Object.is calls on every parent render\nconst HeavyCard = React.memo(function HeavyCard({\n  id, title, description, imageUrl, price, rating,\n  inStock, category, tags, author, createdAt, updatedAt,\n  onClick, onHover, onFocus, isSelected, isFeatured,\n  discount, currency, locale\n}) { … })\n```\n\nIf the child renders in \u003C1 ms and the parent re-renders infrequently, the\nmemo overhead (prop comparison) may cost more than the render it prevents.\n\n**Rule of thumb:** Memoize components with few, stable-typed props. For\ncomponents with many props, the comparison cost can rival the render cost —\nmeasure before committing.\n",14,{"description":32},"React.memo interview questions — when to memoize components, shallow equality, custom comparators, pitfalls, and when memo actually hurts performance.","react\u002Frendering-and-performance\u002Freact-memo","React.memo","7dofqRhUk6xgXgzdBuqwdmg_ndP3khxTUKeZGWjyTnY",{"id":185,"title":186,"body":187,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":191,"navigation":38,"order":193,"path":194,"questions":195,"questionsCount":103,"related":104,"seo":256,"seoDescription":257,"stem":258,"subtopic":259,"topic":19,"topicSlug":21,"updated":109,"__hash__":260},"qa\u002Freact\u002Frendering-and-performance\u002Fusememo-usecallback-patterns.md","Usememo Usecallback Patterns",{"type":29,"value":188,"toc":189},[],{"title":32,"searchDepth":11,"depth":11,"links":190},[],{"subtopicSlug":192},"usememo-usecallback-patterns",3,"\u002Freact\u002Frendering-and-performance\u002Fusememo-usecallback-patterns",[196,200,204,208,212,216,220,224,228,232,236,240,244,248,252],{"id":197,"difficulty":43,"q":198,"a":199},"what-is-usememo","What does useMemo do and what problem does it solve?","`useMemo` is a React hook that **caches the result of a computation**\nbetween renders. It re-runs the factory function only when one of its\nlisted **dependencies** changes; otherwise it returns the previously\ncached value.\n\n```jsx\nfunction ProductList({ products, filterText }) {\n  \u002F\u002F Without useMemo: filtered every render even if products\u002FfilterText\n  \u002F\u002F haven't changed\n  const filtered = useMemo(\n    () => products.filter(p => p.name.includes(filterText)),\n    [products, filterText] \u002F\u002F only re-compute when these change\n  )\n\n  return filtered.map(p => \u003CProductRow key={p.id} product={p} \u002F>)\n}\n```\n\nThe problem it solves is twofold. First, it avoids **re-running\nexpensive computations** on every render. Second, it preserves\n**referential identity** of objects and arrays so downstream\n`React.memo` comparisons and `useEffect` dependency checks don't\nfire unnecessarily.\n\n**Rule of thumb:** Only reach for `useMemo` when the computation is\nmeasurably expensive or when referential stability of the returned\nvalue is required by a consumer — not as a default wrapping strategy.\n",{"id":201,"difficulty":43,"q":202,"a":203},"what-is-usecallback","What does useCallback do and how is it different from useMemo?","`useCallback` **memoizes a function reference** so the same function\nobject is returned across renders as long as its dependencies stay the\nsame. It is syntactic sugar over `useMemo` returning a function.\n\n```jsx\n\u002F\u002F These two are equivalent:\nconst handleClick = useCallback(() => {\n  doSomething(id)\n}, [id])\n\nconst handleClick = useMemo(() => () => {\n  doSomething(id)\n}, [id])\n```\n\nThe key difference in intent:\n- `useMemo` — memoize a **computed value** (object, array, number…)\n- `useCallback` — memoize a **function definition** so its reference\n  stays stable\n\nBoth return a cached result; `useCallback` is just the idiomatic form\nwhen that result is a function.\n\n**Rule of thumb:** Use `useCallback` when you need a stable function\nreference to pass as a prop to a memoized child or as a dependency\ninside a `useEffect` — not to speed up the call itself.\n",{"id":205,"difficulty":43,"q":206,"a":207},"dependency-arrays-explained","How do dependency arrays work in useMemo and useCallback?","The **dependency array** is the second argument to both hooks. React\ncompares each element of the array to its previous value using\n`Object.is` after every render. If any element changed, the cached\nvalue is discarded and the factory function re-runs.\n\n```jsx\nconst total = useMemo(() => {\n  return items.reduce((sum, item) => sum + item.price, 0)\n}, [items]) \u002F\u002F re-compute only when `items` reference changes\n\n\u002F\u002F Common mistakes:\n\u002F\u002F [] — never re-computes (stale data if items changes)\n\u002F\u002F no array — re-computes every render (defeats memoisation)\n\u002F\u002F [items.length] — misses mutations that don't change length\n```\n\nReact's ESLint plugin (`eslint-plugin-react-hooks`) enforces the\n**exhaustive-deps** rule, which flags any value used inside the\nfactory that is missing from the dependency array. This prevents\n**stale closure bugs** at the cost of occasionally breaking\nmemoisation when a dependency changes too often.\n\n**Rule of thumb:** Always list every reactive value read inside the\nfactory; rely on `eslint-plugin-react-hooks` to catch omissions\nautomatically.\n",{"id":209,"difficulty":34,"q":210,"a":211},"referential-stability","What is referential stability and why does it matter for memoization?","**Referential stability** means an object or function returns the\nsame memory reference across renders. In JavaScript, two objects\nwith identical contents are still different values under `Object.is`,\nwhich is what React uses for prop comparison and dependency checks.\n\n```jsx\nfunction Parent() {\n  \u002F\u002F NEW object reference every render — breaks child memo\n  const style = { color: 'red' }\n\n  \u002F\u002F STABLE reference — child memo works\n  const style = useMemo(() => ({ color: 'red' }), [])\n\n  return \u003CMemoizedChild style={style} \u002F>\n}\n\nconst MemoizedChild = React.memo(function Child({ style }) {\n  console.log('render') \u002F\u002F fires every render without useMemo above\n  return \u003Cdiv style={style}>hello\u003C\u002Fdiv>\n})\n```\n\nWithout referential stability, `React.memo` always sees \"new\" props,\n`useEffect` dependency checks always see \"new\" dependencies, and\ndownstream `useMemo`\u002F`useCallback` hooks always invalidate — making\nall memoisation downstream pointless.\n\n**Rule of thumb:** Stabilise object and array props with `useMemo`\nand function props with `useCallback` before passing them into\n`React.memo` components or `useEffect` dependencies.\n",{"id":213,"difficulty":34,"q":214,"a":215},"when-not-to-memoize","When should you NOT use useMemo or useCallback?","Memoisation has a **cost**: React must store the previous value, run\nthe dependency comparison on every render, and keep a closure alive\nin memory. For cheap operations, this overhead exceeds the saving.\n\n```jsx\n\u002F\u002F Bad — wrapping a trivial computation\nconst doubled = useMemo(() => count * 2, [count])\n\n\u002F\u002F Good — just compute it inline\nconst doubled = count * 2\n\n\u002F\u002F Bad — memoizing a callback that is never passed to a memo'd child\nconst handleClick = useCallback(() => setOpen(true), [])\n\n\u002F\u002F Good — inline is fine if the parent re-renders anyway\nconst handleClick = () => setOpen(true)\n```\n\nAvoid `useMemo`\u002F`useCallback` when:\n- The computation is fast (arithmetic, simple string ops)\n- The dependency changes on every render anyway\n- The child receiving the value is not wrapped in `React.memo`\n- You're in early development — premature optimisation obscures intent\n\n**Rule of thumb:** Profile first with React DevTools Profiler; add\nmemoisation only where you can measure a render-time win, not\npreemptively everywhere.\n",{"id":217,"difficulty":34,"q":218,"a":219},"expensive-computation-threshold","How do you decide if a computation is \"expensive enough\" to memoize?","There is no fixed rule, but the common benchmark is **>1 ms** in the\nprofiler during normal usage. React's own docs suggest a rough test:\nwrap the call in `console.time` \u002F `console.timeEnd` and see if it\nconsistently exceeds 1 ms on mid-range hardware.\n\n```jsx\n\u002F\u002F Quick check in development\nconsole.time('filter')\nconst result = hugeList.filter(expensivePredicate)\nconsole.timeEnd('filter') \u002F\u002F e.g. \"filter: 3.4 ms\" → worth memoizing\n\n\u002F\u002F Production pattern\nconst filtered = useMemo(\n  () => hugeList.filter(expensivePredicate),\n  [hugeList, expensivePredicate]\n)\n```\n\nCommon genuinely expensive operations: filtering\u002Fsorting large\narrays (10 000+ items), heavy string processing, recursive tree\ntraversals, and cryptographic hashes. Cheap operations that look\nexpensive but aren't: `.map` over \u003C500 items, simple arithmetic,\nand string concatenation.\n\n**Rule of thumb:** If `console.time` shows \u003C1 ms consistently, skip\n`useMemo` — the hook overhead may actually be slower than recomputing.\n",{"id":221,"difficulty":34,"q":222,"a":223},"usememo-with-react-memo","How do useMemo and React.memo work together?","`React.memo` skips re-rendering a child when **all props** are\nshallowly equal. For that to work, object and function props must\nbe referentially stable — which is exactly what `useMemo` and\n`useCallback` provide in the parent.\n\n```jsx\nconst Chart = React.memo(function Chart({ data, onHover }) {\n  console.log('Chart render')\n  return \u003Ccanvas \u002F> \u002F\u002F expensive canvas draw\n})\n\nfunction Dashboard({ rawData }) {\n  \u002F\u002F Without these, Chart re-renders on every Dashboard render\n  const data = useMemo(\n    () => transformForChart(rawData), \u002F\u002F stable reference\n    [rawData]\n  )\n  const onHover = useCallback(\n    (point) => showTooltip(point),\n    [] \u002F\u002F no deps — stable forever\n  )\n\n  return \u003CChart data={data} onHover={onHover} \u002F>\n}\n```\n\nThe three work as a unit: `React.memo` is the gate; `useMemo` and\n`useCallback` are what keep the keys from changing unnecessarily.\nWithout stable props, `React.memo` is effectively a no-op.\n\n**Rule of thumb:** Whenever you wrap a component in `React.memo`,\naudit its parent for inline objects\u002Ffunctions and stabilise them\nwith `useMemo`\u002F`useCallback`.\n",{"id":225,"difficulty":72,"q":226,"a":227},"stale-closures-in-callbacks","What is a stale closure in useCallback and how do you fix it?","A **stale closure** occurs when a `useCallback` (or `useMemo`)\ncaptures a value from an earlier render because the dependency array\nwas incomplete. The function \"closes over\" the old value and continues\nusing it even after the real value has changed.\n\n```jsx\nfunction Counter() {\n  const [count, setCount] = useState(0)\n\n  \u002F\u002F BUG: count is captured at 0 and never updated\n  const logCount = useCallback(() => {\n    console.log(count) \u002F\u002F always logs 0\n  }, []) \u002F\u002F ← missing `count` dependency\n\n  \u002F\u002F FIX 1: add count to deps (new function ref when count changes)\n  const logCount = useCallback(() => {\n    console.log(count)\n  }, [count])\n\n  \u002F\u002F FIX 2: use functional updater when only setting state\n  const increment = useCallback(() => {\n    setCount(prev => prev + 1) \u002F\u002F no stale closure — reads latest\n  }, [])\n\n  return \u003Cbutton onClick={logCount}>Log {count}\u003C\u002Fbutton>\n}\n```\n\nThe ESLint `exhaustive-deps` rule catches most stale closures at\nlint time. For callbacks that need to always read the latest value\nwithout changing reference, consider the **ref-callback pattern**:\nstore the latest function in a ref and call it from a stable wrapper.\n\n**Rule of thumb:** Never omit a dependency to \"keep the function\nstable\" — either add it honestly or use `useReducer` \u002F functional\nupdates to remove the dependency legitimately.\n",{"id":229,"difficulty":34,"q":230,"a":231},"object-memoization-patterns","What are the common patterns for memoizing objects and derived data?","Three situations call for object memoisation:\n\n1. **Derived data** — transform props into a new object for rendering\n2. **Config objects** — pass stable option bags to child components\n3. **Context values** — prevent all consumers from re-rendering\n\n```jsx\nfunction UserCard({ user, theme }) {\n  \u002F\u002F 1. Derived data\n  const displayName = useMemo(\n    () => `${user.firstName} ${user.lastName}`.trim(),\n    [user.firstName, user.lastName]\n  )\n\n  \u002F\u002F 2. Stable config object for a memoized child\n  const chartOptions = useMemo(\n    () => ({ color: theme.primary, legend: false }),\n    [theme.primary]\n  )\n\n  \u002F\u002F 3. Context value — prevents all consumers re-rendering\n  const ctx = useMemo(\n    () => ({ user, displayName }),\n    [user, displayName]\n  )\n\n  return (\n    \u003CUserContext.Provider value={ctx}>\n      \u003CMemoizedChart options={chartOptions} \u002F>\n    \u003C\u002FUserContext.Provider>\n  )\n}\n```\n\nAvoid memoizing objects whose dependencies change on every render —\nyou get the cost of comparison with none of the saving.\n\n**Rule of thumb:** Memoize objects when you control neither the\nre-render frequency of the parent nor the comparison logic of the\nconsumer — context providers and `React.memo` children are the\ncanonical cases.\n",{"id":233,"difficulty":34,"q":234,"a":235},"inline-vs-extracted-callbacks","Should callbacks be inlined or extracted with useCallback?","The answer depends entirely on whether the callback is passed to a\n**memoized child** or used as a **`useEffect` dependency**.\n\n```jsx\nfunction Form() {\n  const [value, setValue] = useState('')\n\n  \u002F\u002F INLINE — fine: onChange is not passed to a memo'd child\n  \u002F\u002F and causes no extra effects\n  return (\n    \u003Cinput\n      onChange={e => setValue(e.target.value)} \u002F\u002F new ref each render\n      value={value}\n    \u002F>\n  )\n}\n\nfunction SearchPanel({ onSearch }) {\n  \u002F\u002F EXTRACTED — necessary: ResultList is React.memo'd\n  const handleSearch = useCallback(() => {\n    onSearch(query)\n  }, [onSearch, query])\n\n  return \u003CMemoizedResultList onSearch={handleSearch} \u002F>\n}\n```\n\nInline lambdas are idiomatic and readable for event handlers on\nDOM elements. Extracting with `useCallback` adds noise and overhead\nwhen the consumer doesn't benefit from a stable reference.\n\n**Rule of thumb:** Default to inline; extract with `useCallback`\nonly when the callback flows into a `React.memo` boundary or a\n`useEffect` \u002F `useMemo` dependency list.\n",{"id":237,"difficulty":72,"q":238,"a":239},"memoizing-callbacks-in-custom-hooks","Why should custom hooks wrap returned callbacks in useCallback?","A custom hook's returned functions become **dependencies** for any\n`useEffect` or `useCallback` in the consuming component. If the\nhook returns a new function reference on every call, the consumer\nis forced to either omit it from deps (stale closure risk) or accept\nspurious effect re-runs.\n\n```jsx\n\u002F\u002F Bad — new reference every render\nfunction useSearch(query) {\n  const search = () => fetchResults(query) \u002F\u002F new fn each time\n  return { search }\n}\n\n\u002F\u002F Good — stable reference, updates only when query changes\nfunction useSearch(query) {\n  const search = useCallback(\n    () => fetchResults(query),\n    [query]\n  )\n  return { search }\n}\n\n\u002F\u002F Consumer can now safely list `search` as a dep\nfunction SearchBox({ query }) {\n  const { search } = useSearch(query)\n\n  useEffect(() => {\n    search() \u002F\u002F no stale closure, no spurious re-runs\n  }, [search])\n}\n```\n\nThis is why popular hook libraries (React Query, SWR, Zustand)\nmemoize every returned function — it makes composition predictable\nwithout requiring callers to know the hook's internals.\n\n**Rule of thumb:** Treat functions returned from custom hooks the\nsame as public API — wrap them in `useCallback` so consumers can\nsafely include them in dependency arrays.\n",{"id":241,"difficulty":72,"q":242,"a":243},"usememo-context-rerenders","How can useMemo prevent unnecessary context consumer re-renders?","Every time a `Context.Provider` re-renders, **all consumers**\nre-render unless the context `value` is referentially the same\nobject. Without `useMemo`, a provider's value object is recreated\non every parent render, making the context update cascade\nunavoidable.\n\n```jsx\n\u002F\u002F Bad — new object on every Parent render\nfunction AuthProvider({ children }) {\n  const [user, setUser] = useState(null)\n  return (\n    \u003CAuthContext.Provider value={{ user, setUser }}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  )\n}\n\n\u002F\u002F Good — stable object; consumers only re-render when user changes\nfunction AuthProvider({ children }) {\n  const [user, setUser] = useState(null)\n  const value = useMemo(\n    () => ({ user, setUser }),\n    [user] \u002F\u002F setUser is stable from useState, safe to omit\n  )\n  return (\n    \u003CAuthContext.Provider value={value}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  )\n}\n```\n\nFor large apps, splitting context into a read context and a write\ncontext (each with its own `useMemo` value) further reduces\nre-renders: write-only consumers don't re-render when the user data\nchanges.\n\n**Rule of thumb:** Always wrap a context `value` in `useMemo` in\nproviders that live high in the tree — the blast radius of a\nneedless update is proportional to how many consumers are below it.\n",{"id":245,"difficulty":72,"q":246,"a":247},"memoization-correctness-pitfalls","What are the most common memoization correctness pitfalls in React?","Memoisation can introduce subtle bugs when the dependency array is\nwrong or when the memoized value is mutated instead of replaced.\n\n```jsx\n\u002F\u002F PITFALL 1: mutating the cached value\nconst items = useMemo(() => [], [])\nitems.push(newItem) \u002F\u002F ← mutates; useMemo won't notice, UI goes stale\n\n\u002F\u002F Fix: always return a new array\nconst items = useMemo(() => [...baseItems, newItem], [baseItems, newItem])\n\n\u002F\u002F PITFALL 2: object identity as a dep (always \"new\")\nconst options = { limit: 10 }\nconst result = useMemo(() => query(options), [options]) \u002F\u002F re-runs every render\n\n\u002F\u002F Fix: depend on primitives, not the object\nconst result = useMemo(() => query({ limit: 10 }), []) \u002F\u002F or [limit]\n\n\u002F\u002F PITFALL 3: async factory (useMemo is synchronous only)\nconst data = useMemo(async () => fetchData(), []) \u002F\u002F returns a Promise, not data\n\n\u002F\u002F Fix: use useEffect + useState for async work\n```\n\nThese pitfalls share a root cause: treating `useMemo` as smarter\nthan it is. It only tracks the dependency array — it cannot detect\nmutation or understand async semantics.\n\n**Rule of thumb:** Treat memoized values as **immutable**; never\nmutate them in place, depend only on primitives or already-stable\nreferences, and keep factory functions synchronous.\n",{"id":249,"difficulty":72,"q":250,"a":251},"ref-callback-pattern","What is the ref-callback pattern and when does it replace useCallback?","The **ref-callback pattern** (sometimes called \"latest ref\") keeps\na `useCallback` reference stable while still reading the latest\nclosure values. It solves the tension between \"stable function\" and\n\"always current data\".\n\n```jsx\nfunction useLatestCallback(fn) {\n  const ref = useRef(fn)\n  \u002F\u002F keep the ref up to date on every render (safe: layout effect)\n  useLayoutEffect(() => { ref.current = fn })\n  \u002F\u002F return a stable wrapper that delegates to the latest fn\n  return useCallback((...args) => ref.current(...args), [])\n}\n\nfunction SearchBox({ onSearch }) {\n  \u002F\u002F onSearch may change often, but handleSearch is always stable\n  const handleSearch = useLatestCallback(onSearch)\n\n  useEffect(() => {\n    const id = setInterval(handleSearch, 5000)\n    return () => clearInterval(id)\n  }, [handleSearch]) \u002F\u002F no stale closure, no interval reset on each render\n}\n```\n\nThis pattern is useful for event handlers and interval callbacks\nthat must be stable but need to capture the very latest state or\nprops. React's upcoming `useEffectEvent` hook formalises this\npattern in the framework itself.\n\n**Rule of thumb:** Reach for the ref-callback pattern when\n`useCallback` forces an awkward choice between adding a fast-moving\ndep (breaking stability) and omitting it (creating a stale closure).\n",{"id":253,"difficulty":34,"q":254,"a":255},"usecallback-with-event-handlers","Do event handlers on DOM elements need useCallback?","No. Native DOM event handlers (on `\u003Cbutton>`, `\u003Cinput>`, etc.) do\n**not** benefit from `useCallback`. React attaches DOM events at\nthe root via event delegation — the function reference you pass is\nnever directly stored on the DOM node, and changing it between\nrenders has zero cost.\n\n```jsx\nfunction Counter() {\n  const [count, setCount] = useState(0)\n\n  \u002F\u002F Unnecessary — the \u003Cbutton> doesn't care about reference stability\n  const handleClick = useCallback(\n    () => setCount(c => c + 1),\n    []\n  )\n\n  \u002F\u002F Equivalent and simpler\n  const handleClick = () => setCount(c => c + 1)\n\n  \u002F\u002F useCallback IS needed when handleClick flows into a memo'd child\n  return (\n    \u003C>\n      \u003Cbutton onClick={handleClick}>{count}\u003C\u002Fbutton>\n      \u003CMemoizedExpensiveChild onAction={handleClick} \u002F>\n    \u003C\u002F>\n  )\n}\n```\n\nThe second `\u003CMemoizedExpensiveChild>` case justifies `useCallback`\nbecause `React.memo`'s prop comparison is what creates the\nstability requirement — the DOM element does not.\n\n**Rule of thumb:** If the only consumer of a callback is a plain\nDOM element (not a `React.memo` component), skip `useCallback` —\nit adds complexity with no measurable benefit.\n",{"description":32},"React useMemo and useCallback interview questions — memoization patterns, dependency arrays, when to use each hook, referential stability, and common pitfalls.","react\u002Frendering-and-performance\u002Fusememo-usecallback-patterns","useMemo and useCallback Patterns","qvgFVw09_MDmKpdnJo5Rk2LaOnMGjDsmC8vmk7xeLN4",{"id":262,"title":263,"body":264,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":268,"navigation":38,"order":20,"path":270,"questions":271,"questionsCount":103,"related":104,"seo":332,"seoDescription":333,"stem":334,"subtopic":335,"topic":19,"topicSlug":21,"updated":109,"__hash__":336},"qa\u002Freact\u002Frendering-and-performance\u002Fcode-splitting-lazy.md","Code Splitting Lazy",{"type":29,"value":265,"toc":266},[],{"title":32,"searchDepth":11,"depth":11,"links":267},[],{"subtopicSlug":269},"code-splitting-lazy","\u002Freact\u002Frendering-and-performance\u002Fcode-splitting-lazy",[272,276,280,284,288,292,296,300,304,308,312,316,320,324,328],{"id":273,"difficulty":43,"q":274,"a":275},"what-is-code-splitting","What is code splitting and why does it matter in React?","**Code splitting** is the practice of breaking a JavaScript bundle into\nsmaller **chunks** that are loaded on demand instead of shipping everything\nin one large file. Without it, every user downloads your entire app upfront —\neven code for routes or features they never visit.\n\n```jsx\n\u002F\u002F Without splitting — one giant bundle (bad for initial load)\nimport HeavyDashboard from '.\u002FHeavyDashboard'\nimport ReportsPDF from '.\u002FReportsPDF'\n\n\u002F\u002F With splitting — each module loads only when needed\nconst HeavyDashboard = React.lazy(() => import('.\u002FHeavyDashboard'))\nconst ReportsPDF     = React.lazy(() => import('.\u002FReportsPDF'))\n```\n\nThe browser only fetches a chunk when the user triggers the code path that\nneeds it, dramatically reducing **Time to Interactive (TTI)** and\n**First Contentful Paint (FCP)** for the initial page. Tools like Webpack,\nVite, and Rollup split bundles automatically at every `import()` call.\n\nBenefits include faster initial load, smaller parse\u002Fcompile work on the main\nthread, and better cache granularity — a chunk for a rarely-changed library\nwon't bust the cache when your app logic changes.\n\n**Rule of thumb:** Split at route boundaries first — that alone cuts most\napps' initial bundle by 30–60 % without any layout complexity.\n",{"id":277,"difficulty":43,"q":278,"a":279},"react-lazy-dynamic-import","How do React.lazy and dynamic import() work together?","`React.lazy` accepts a **factory function** that returns a promise of a\nmodule with a **default export** that is a React component. Under the hood\nit wraps the dynamic `import()` expression, which is a native browser\u002Fbundler\nfeature that triggers on-demand chunk loading.\n\n```jsx\nimport React, { lazy, Suspense } from 'react'\n\n\u002F\u002F dynamic import() returns Promise\u003Cmodule>\n\u002F\u002F React.lazy unwraps the default export for you\nconst SettingsPage = lazy(() => import('.\u002Fpages\u002FSettingsPage'))\n\nfunction App() {\n  return (\n    \u003CSuspense fallback={\u003Cdiv>Loading settings…\u003C\u002Fdiv>}>\n      \u003CSettingsPage \u002F>\n    \u003C\u002FSuspense>\n  )\n}\n```\n\nWhen React first tries to render `\u003CSettingsPage \u002F>`, it kicks off the\n`import()` call. While the network request is in flight, React throws a\n**promise** (internally), and the nearest `\u003CSuspense>` boundary catches it\nand renders the `fallback`. Once the chunk resolves, React re-renders with\nthe real component.\n\nThe bundle tool (Webpack\u002FVite) sees the `import()` and automatically creates\na separate output chunk at build time.\n\n**Rule of thumb:** Always wrap the factory in an arrow function —\n`lazy(import('.\u002FFoo'))` executes immediately and defeats lazy loading.\n",{"id":281,"difficulty":43,"q":282,"a":283},"suspense-boundary-required","Why is a Suspense boundary required when using React.lazy?","`React.lazy` components are **asynchronous** — they need to fetch their\nchunk from the network before they can render. React's Suspense mechanism\nlets a component signal \"I'm not ready yet\" by throwing a promise. Without a\n`\u003CSuspense>` ancestor to catch that signal, React has nowhere to show a\nloading state and will throw an unhandled error instead.\n\n```jsx\n\u002F\u002F ❌ Missing Suspense — throws at runtime\nfunction App() {\n  return \u003CLazyChart \u002F>  \u002F\u002F React.lazy component with no Suspense ancestor\n}\n\n\u002F\u002F ✅ Suspense boundary catches the pending state\nfunction App() {\n  return (\n    \u003CSuspense fallback={\u003CSpinner \u002F>}>\n      \u003CLazyChart \u002F>\n    \u003C\u002FSuspense>\n  )\n}\n```\n\nYou can place the boundary anywhere above the lazy component in the tree.\nNesting multiple boundaries lets you control fallback granularity — a\ntop-level boundary shows a full-page spinner while inner boundaries show\nsmaller skeletons for individual sections.\n\n**Rule of thumb:** Put `\u003CSuspense>` as close to the lazy component as\nmakes sense for your UX — the tighter the boundary, the less UI is\nreplaced by the fallback.\n",{"id":285,"difficulty":34,"q":286,"a":287},"route-based-vs-component-based-splitting","What is the difference between route-based and component-based code splitting?","**Route-based splitting** loads an entire page component only when the user\nnavigates to that route. It is the highest-leverage split because each route\nis a natural isolation boundary and users rarely visit every route in one\nsession.\n\n**Component-based splitting** defers loading of a single heavy widget\n(e.g., a rich-text editor, chart library, or PDF viewer) until it is\nactually rendered on the page, regardless of route.\n\n```jsx\n\u002F\u002F Route-based — whole pages are lazy\nconst Home     = lazy(() => import('.\u002Fpages\u002FHome'))\nconst Dashboard = lazy(() => import('.\u002Fpages\u002FDashboard'))\n\n\u003CRoutes>\n  \u003CRoute path=\"\u002F\" element={\n    \u003CSuspense fallback={\u003CPageSpinner \u002F>}>\u003CHome \u002F>\u003C\u002FSuspense>\n  } \u002F>\n  \u003CRoute path=\"\u002Fdashboard\" element={\n    \u003CSuspense fallback={\u003CPageSpinner \u002F>}>\u003CDashboard \u002F>\u003C\u002FSuspense>\n  } \u002F>\n\u003C\u002FRoutes>\n\n\u002F\u002F Component-based — a heavy widget on one page\nconst RichEditor = lazy(() => import('.\u002FRichEditor'))\n\nfunction PostEditor({ showEditor }) {\n  return showEditor\n    ? \u003CSuspense fallback={\u003CEditorSkeleton \u002F>}>\u003CRichEditor \u002F>\u003C\u002FSuspense>\n    : \u003CSimplePlaceholder \u002F>\n}\n```\n\nRoute-based is always the first step; component-based is used when a single\nroute still bundles too much.\n\n**Rule of thumb:** Start with route-based splitting; add component-based\nsplitting only when bundle analysis identifies a specific heavy dependency\non a single route.\n",{"id":289,"difficulty":43,"q":290,"a":291},"suspense-fallback-prop","What should you pass to the Suspense fallback prop?","The `fallback` prop accepts **any renderable React node** — a string, JSX,\na spinner component, or a skeleton layout. It renders while the lazy chunk\nis loading and is replaced by the real content once the import resolves.\n\n```jsx\n\u002F\u002F Simple text fallback\n\u003CSuspense fallback=\"Loading…\">\n  \u003CLazyComponent \u002F>\n\u003C\u002FSuspense>\n\n\u002F\u002F Spinner component\n\u003CSuspense fallback={\u003CSpinner size=\"lg\" \u002F>}>\n  \u003CLazyComponent \u002F>\n\u003C\u002FSuspense>\n\n\u002F\u002F Skeleton that matches the real layout (best UX)\n\u003CSuspense fallback={\u003CDashboardSkeleton \u002F>}>\n  \u003CLazyDashboard \u002F>\n\u003C\u002FSuspense>\n\n\u002F\u002F null — renders nothing while loading (use with care)\n\u003CSuspense fallback={null}>\n  \u003CLazyModal \u002F>\n\u003C\u002FSuspense>\n```\n\nA skeleton that mirrors the real component's layout reduces **Cumulative\nLayout Shift (CLS)** because the page structure does not jump when the real\ncontent appears. Avoid heavy fallbacks that are themselves expensive to\nrender.\n\nThe fallback is only shown on the **first** load of a chunk; subsequent\nrenders use the cached module and skip the fallback entirely.\n\n**Rule of thumb:** Match the fallback's dimensions to the real component to\nprevent layout shift — even a simple fixed-height `\u003Cdiv>` is better than\nnothing.\n",{"id":293,"difficulty":34,"q":294,"a":295},"named-exports-with-lazy","React.lazy only works with default exports. How do you lazy-load a named export?","`React.lazy` requires the resolved module to have a **default export**. For\ncomponents exported as named exports, wrap the import in an intermediate\nre-export or inline re-map inside the factory function.\n\n```jsx\n\u002F\u002F components\u002FCharts.tsx — named export\nexport function BarChart() { \u002F* … *\u002F }\nexport function LineChart() { \u002F* … *\u002F }\n\n\u002F\u002F Option 1: inline re-map in the factory\nconst BarChart = lazy(() =>\n  import('.\u002Fcomponents\u002FCharts').then(mod => ({ default: mod.BarChart }))\n)\n\n\u002F\u002F Option 2: create a thin re-export file (Charts.BarChart.ts)\n\u002F\u002F Charts.BarChart.ts\nexport { BarChart as default } from '.\u002FCharts'\n\n\u002F\u002F then lazy-import that file normally\nconst BarChart = lazy(() => import('.\u002Fcomponents\u002FCharts.BarChart'))\n```\n\nBoth approaches satisfy React.lazy's contract of a promise that resolves to\n`{ default: Component }`. The inline `.then()` approach is convenient for\none-offs; a separate re-export file is cleaner when you lazy-load the same\nnamed export in multiple places.\n\n**Rule of thumb:** Prefer Option 1 for a single use case and Option 2 when\nthe same named export is lazy-loaded in more than two places — it avoids\nrepetitive `.then()` boilerplate.\n",{"id":297,"difficulty":34,"q":298,"a":299},"error-boundaries-with-lazy","How should you handle errors when a lazy component fails to load?","If the network request for a lazy chunk fails (e.g., 404, offline), the\nthrown promise rejects and React propagates an **error** up the tree.\nA `\u003CSuspense>` boundary does not catch errors — you need a separate\n**Error Boundary** component wrapping the lazy component.\n\n```jsx\nimport { Component } from 'react'\n\nclass ChunkErrorBoundary extends Component {\n  state = { hasError: false }\n\n  static getDerivedStateFromError() {\n    return { hasError: true }\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return (\n        \u003Cbutton onClick={() => this.setState({ hasError: false })}>\n          Retry\n        \u003C\u002Fbutton>\n      )\n    }\n    return this.props.children\n  }\n}\n\n\u002F\u002F Wrap lazy component with both boundaries\n\u003CChunkErrorBoundary>\n  \u003CSuspense fallback={\u003CSpinner \u002F>}>\n    \u003CLazySettings \u002F>\n  \u003C\u002FSuspense>\n\u003C\u002FChunkErrorBoundary>\n```\n\nThe error boundary must sit **outside** `\u003CSuspense>` so it can catch chunk\nfetch errors after Suspense has already caught the pending promise. Libraries\nlike `react-error-boundary` provide a hook-friendly API for this pattern.\n\n**Rule of thumb:** Always pair `React.lazy` with an error boundary in\nproduction — chunk load failures are real and users deserve a recovery path,\nnot a blank screen.\n",{"id":301,"difficulty":34,"q":302,"a":303},"preloading-lazy-components","How can you preload a lazy component before the user navigates to it?","Because `React.lazy` wraps a factory function, you can **trigger the\nimport manually** before React needs to render the component. The browser\nfetches and caches the chunk, so by the time the component mounts, the\nmodule is already available and the Suspense fallback is skipped.\n\n```jsx\nconst LazyDashboard = lazy(() => import('.\u002FDashboard'))\n\n\u002F\u002F Preload on hover — user shows intent before clicking\nfunction NavLink() {\n  const preload = () => import('.\u002FDashboard')  \u002F\u002F same import() as lazy\n  return (\n    \u003Ca\n      href=\"\u002Fdashboard\"\n      onMouseEnter={preload}   \u002F\u002F starts fetch on hover\n      onFocus={preload}        \u002F\u002F keyboard nav support\n    >\n      Dashboard\n    \u003C\u002Fa>\n  )\n}\n\n\u002F\u002F Or preload after idle time with requestIdleCallback\nif (typeof requestIdleCallback !== 'undefined') {\n  requestIdleCallback(() => import('.\u002FDashboard'))\n}\n```\n\nCall the same dynamic `import()` path used inside `lazy()` — the bundler\nand browser cache the result by URL, so the second call (from React.lazy)\nis instant.\n\n**Rule of thumb:** Preload on `mouseenter` or `focus` for navigation links —\nyou get a ~100–300 ms head start for free, and most users never notice the\nchunk was loaded proactively.\n",{"id":305,"difficulty":34,"q":306,"a":307},"magic-comments-chunk-naming","How do you control the output chunk name for a lazy import?","Webpack and Vite both support **magic comments** inside `import()` to set\nthe output filename and other chunk behavior. The most common is\n`\u002F* webpackChunkName: \"…\" *\u002F` (Webpack) or `\u002F* @vite-ignore *\u002F` (Vite, for\nsuppressing warnings).\n\n```jsx\n\u002F\u002F Webpack — names the output chunk \"dashboard~chunk.js\"\nconst Dashboard = lazy(\n  () => import(\u002F* webpackChunkName: \"dashboard\" *\u002F '.\u002Fpages\u002FDashboard')\n)\n\n\u002F\u002F Webpack prefetch hint — browser fetches in idle time after initial load\nconst Reports = lazy(\n  () => import(\n    \u002F* webpackChunkName: \"reports\" *\u002F\n    \u002F* webpackPrefetch: true *\u002F\n    '.\u002Fpages\u002FReports'\n  )\n)\n\n\u002F\u002F Webpack preload hint — fetches in parallel with parent chunk\nconst HeroChart = lazy(\n  () => import(\n    \u002F* webpackChunkName: \"hero-chart\" *\u002F\n    \u002F* webpackPreload: true *\u002F\n    '.\u002Fcomponents\u002FHeroChart'\n  )\n)\n```\n\nNamed chunks are easier to identify in bundle reports and give cache-friendly\nfilenames. `webpackPrefetch` emits a `\u003Clink rel=\"prefetch\">` tag;\n`webpackPreload` emits `\u003Clink rel=\"preload\">` — use preload sparingly as it\ncompetes with critical resources.\n\n**Rule of thumb:** Name every lazy chunk — anonymous hashes like `3.js`\nare useless in production error logs and bundle analysis tools.\n",{"id":309,"difficulty":43,"q":310,"a":311},"impact-on-initial-load-time","How does code splitting reduce initial load time?","Without splitting, the browser must **download, parse, and compile** the\nentire JavaScript bundle before any interactive content appears. Code\nsplitting reduces all three phases for the initial visit by shipping only\nthe code needed for the landing route.\n\n```\nBefore splitting:\n  Bundle: 1.2 MB  →  parse: 800 ms  →  TTI: 3.2 s\n\nAfter route-based splitting:\n  Initial chunk: 280 KB  →  parse: 180 ms  →  TTI: 1.1 s\n  \u002Fdashboard chunk: 420 KB  ← loaded only when user navigates there\n  \u002Freports chunk:   500 KB  ← loaded only when user navigates there\n```\n\n```jsx\n\u002F\u002F Each lazy() call becomes a separate network request only on demand\nconst Dashboard = lazy(() => import(\u002F* webpackChunkName: \"dashboard\" *\u002F '.\u002FDashboard'))\nconst Reports   = lazy(() => import(\u002F* webpackChunkName: \"reports\"   *\u002F '.\u002FReports'))\n\n\u002F\u002F First paint only fetches the initial chunk\n\u002F\u002F Dashboard and Reports chunks are fetched on navigation\n```\n\nThe savings compound with caching: once a user has visited `\u002Fdashboard`,\nthe chunk is cached and subsequent navigations are instant.\n\n**Rule of thumb:** Measure before and after with `npm run build -- --report`\nor Vite's `--reporter` flag — aim to keep your initial chunk under 200 KB\ngzipped.\n",{"id":313,"difficulty":72,"q":314,"a":315},"lazy-loading-in-ssr","What are the limitations of React.lazy in a server-side rendering (SSR) environment?","`React.lazy` is **not supported in SSR** with React 17 and earlier — it only\nworks in the browser. On the server, calling a lazy component throws because\nthere is no Suspense-compatible server renderer to handle the dynamic import.\n\nReact 18's `renderToPipeableStream` and `renderToReadableStream` do support\nSuspense on the server, but `React.lazy` itself still requires the module to\nbe available synchronously during SSR unless you use a framework that handles\nit.\n\n```jsx\n\u002F\u002F For SSR, use framework-native lazy loading instead:\n\n\u002F\u002F Next.js — next\u002Fdynamic handles SSR by default\nimport dynamic from 'next\u002Fdynamic'\n\nconst HeavyChart = dynamic(() => import('.\u002FHeavyChart'), {\n  loading: () => \u003CChartSkeleton \u002F>,\n  ssr: false,   \u002F\u002F skip server render entirely for browser-only widgets\n})\n\n\u002F\u002F Remix — uses route-level splitting natively via its file-system router\n\u002F\u002F No extra config needed — every route file is a split point\n```\n\nFor framework-agnostic SSR, `@loadable\u002Fcomponent` is the standard solution —\nit serializes which chunks were used on the server and preloads them on the\nclient to avoid hydration mismatches.\n\n**Rule of thumb:** In SSR apps, reach for your framework's dynamic import\nhelper (Next.js `dynamic`, Remix routes, Nuxt `defineAsyncComponent`) rather\nthan bare `React.lazy`.\n",{"id":317,"difficulty":34,"q":318,"a":319},"multiple-lazy-one-suspense","Can multiple lazy components share a single Suspense boundary?","Yes. A single `\u003CSuspense>` boundary handles **all** lazy descendants — it\nshows the fallback until **every** lazy child in its subtree has resolved.\nThis is fine when you want a single loading state for a group of components\nthat logically appear together.\n\n```jsx\nconst Sidebar  = lazy(() => import('.\u002FSidebar'))\nconst MainFeed = lazy(() => import('.\u002FMainFeed'))\nconst Widgets  = lazy(() => import('.\u002FWidgets'))\n\n\u002F\u002F One boundary — fallback shows until ALL three chunks are ready\n\u003CSuspense fallback={\u003CPageSkeleton \u002F>}>\n  \u003CSidebar \u002F>\n  \u003CMainFeed \u002F>\n  \u003CWidgets \u002F>\n\u003C\u002FSuspense>\n\n\u002F\u002F Nested boundaries — each section shows its own fallback independently\n\u003CSuspense fallback={\u003CSidebarSkeleton \u002F>}>\n  \u003CSidebar \u002F>\n\u003C\u002FSuspense>\n\u003CSuspense fallback={\u003CFeedSkeleton \u002F>}>\n  \u003CMainFeed \u002F>\n  \u003CWidgets \u002F>   {\u002F* Widgets shares MainFeed's boundary *\u002F}\n\u003C\u002FSuspense>\n```\n\nMultiple lazy components under one boundary also means all their network\nrequests fire in **parallel** — React does not wait for one to finish before\nstarting the next.\n\n**Rule of thumb:** Share a single boundary when the components form one\nvisual unit; nest separate boundaries when different sections can\nindependently become interactive.\n",{"id":321,"difficulty":34,"q":322,"a":323},"react-lazy-limitations","What are the key limitations of React.lazy?","`React.lazy` is powerful but has several constraints worth knowing for\ninterviews:\n\n```jsx\n\u002F\u002F 1. Default exports only — named exports need re-mapping\nconst Foo = lazy(() => import('.\u002FFoo'))               \u002F\u002F ✅\nconst Bar = lazy(() => import('.\u002FBar').then(m => ({ default: m.Bar }))) \u002F\u002F ✅ workaround\n\n\u002F\u002F 2. Must be called at module level, not inside render\nfunction BadParent() {\n  \u002F\u002F ❌ Creates a new lazy reference every render — loses state and re-fetches\n  const Lazy = lazy(() => import('.\u002FChild'))\n  return \u003CLazy \u002F>\n}\n\n\u002F\u002F 3. No server-side rendering support (React 17 and earlier)\n\u002F\u002F Use next\u002Fdynamic or @loadable\u002Fcomponent in SSR apps\n\n\u002F\u002F 4. Requires a Suspense boundary — no built-in fallback\n\u002F\u002F 5. No built-in error handling — needs an Error Boundary\n```\n\nAdditional limitations: you cannot lazy-load hooks or context providers\n(only renderable components), and `React.lazy` does not support passing\noptions like timeout or retry logic — those require custom wrappers.\n\n**Rule of thumb:** Declare `lazy()` at module scope, not inside component\nbodies — creating a new lazy reference on every render causes the chunk to\nre-fetch and the component to remount, destroying local state.\n",{"id":325,"difficulty":72,"q":326,"a":327},"splitting-third-party-libraries","How do you code-split heavy third-party libraries in React?","Third-party packages imported at the top of a file are bundled into\nwhichever chunk imports them. To split them out, import them **dynamically**\ninside the component or effect that needs them instead of at the top level.\n\n```jsx\n\u002F\u002F ❌ Static import — lands in the initial bundle even if rarely used\nimport { Chart } from 'chart.js'\n\n\u002F\u002F ✅ Dynamic import — chunk created only for routes that use Chart\nfunction SalesChart({ data }) {\n  const [ChartLib, setChartLib] = useState(null)\n\n  useEffect(() => {\n    import('chart.js').then(mod => setChartLib(() => mod.Chart))\n  }, [])\n\n  if (!ChartLib) return \u003CChartSkeleton \u002F>\n  return \u003CChartLib data={data} \u002F>\n}\n\n\u002F\u002F ✅ Better: wrap with React.lazy for Suspense support\nconst PDFViewer = lazy(() =>\n  import('react-pdf').then(mod => ({ default: mod.Document }))\n)\n\n\u002F\u002F ✅ Webpack vendor chunk — group stable libs into a long-cached chunk\n\u002F\u002F In webpack.config.js:\n\u002F\u002F optimization.splitChunks.cacheGroups.vendors: { test: \u002Fnode_modules\u002F }\n```\n\nFor libraries like Moment.js or lodash, also check if tree-shaking and\nlighter alternatives (date-fns, lodash-es) eliminate the need to split at all.\n\n**Rule of thumb:** Check `bundle-analyzer` output first — if a library\naccounts for over 50 KB gzipped in your initial bundle and is not needed on\nthe landing page, it is a prime split candidate.\n",{"id":329,"difficulty":34,"q":330,"a":331},"measuring-bundle-size-impact","How do you measure the impact of code splitting on bundle size?","Three complementary tools cover the full picture: the bundler's built-in\nstats, a visual analyzer, and a real-browser network trace.\n\n```bash\n# Webpack — generate stats.json then open in webpack-bundle-analyzer\nnpx webpack --profile --json > stats.json\nnpx webpack-bundle-analyzer stats.json\n\n# Vite — built-in rollup visualizer plugin\n# vite.config.ts\nimport { visualizer } from 'rollup-plugin-visualizer'\nplugins: [react(), visualizer({ open: true, gzipSize: true })]\n\n# CRA — source-map-explorer\nnpm run build\nnpx source-map-explorer 'build\u002Fstatic\u002Fjs\u002F*.js'\n```\n\n```jsx\n\u002F\u002F After splitting, compare chunk sizes in the build output:\n\u002F\u002F dist\u002Fassets\u002Findex-3f2a.js      → 87 KB  (initial)\n\u002F\u002F dist\u002Fassets\u002Fdashboard-9c1b.js  → 142 KB (on demand)\n\u002F\u002F dist\u002Fassets\u002Freports-4d7e.js    → 198 KB (on demand)\n\u002F\u002F Initial savings: 340 KB removed from first load\n```\n\nAlso check the **Network tab** in DevTools with throttling enabled — confirm\nthat on-demand chunks appear as separate requests timed to navigation, not\non initial page load.\n\n**Rule of thumb:** Run bundle analysis in CI and set a size budget (e.g.,\n`bundlesize` or Vite's `build.chunkSizeWarningLimit`) — that way regressions\nare caught before they reach production.\n",{"description":32},"React code splitting and lazy loading interview questions — React.lazy, dynamic imports, Suspense boundaries, route-based splitting, bundle optimization, and prefetching strategies.","react\u002Frendering-and-performance\u002Fcode-splitting-lazy","Code Splitting and Lazy Loading","wV_XCzM0ToidDuok6CjeIfLx02W6hMUv62fftXfy7Sc",{"id":338,"title":339,"body":340,"description":32,"difficulty":72,"extension":35,"framework":10,"frameworkSlug":8,"meta":344,"navigation":38,"order":346,"path":347,"questions":348,"questionsCount":103,"related":104,"seo":408,"seoDescription":409,"stem":410,"subtopic":411,"topic":19,"topicSlug":21,"updated":109,"__hash__":412},"qa\u002Freact\u002Frendering-and-performance\u002Fsuspense-concurrent.md","Suspense Concurrent",{"type":29,"value":341,"toc":342},[],{"title":32,"searchDepth":11,"depth":11,"links":343},[],{"subtopicSlug":345},"suspense-concurrent",5,"\u002Freact\u002Frendering-and-performance\u002Fsuspense-concurrent",[349,353,356,360,364,368,372,376,380,384,388,392,396,400,404],{"id":350,"difficulty":43,"q":351,"a":352},"what-does-suspense-do","What does React Suspense do?","**React Suspense** is a mechanism that lets a component declare that it is\nnot ready to render yet. While the component is suspended, React renders a\n**fallback** UI in its place and picks up where it left off once the\ncomponent is ready.\n\n```jsx\nimport { Suspense, lazy } from 'react'\n\nconst Chart = lazy(() => import('.\u002FChart'))   \u002F\u002F code-split chunk\n\nfunction Dashboard() {\n  return (\n    \u003CSuspense fallback={\u003Cp>Loading chart…\u003C\u002Fp>}>\n      {\u002F* React renders the fallback while Chart's chunk downloads *\u002F}\n      \u003CChart \u002F>\n    \u003C\u002FSuspense>\n  )\n}\n```\n\nSuspense works by catching a special **thrown Promise**. When a component\nthrows a Promise, React walks up the tree looking for the nearest\n`\u003CSuspense>` boundary. That boundary shows its `fallback` until the\nPromise resolves, then re-attempts to render the suspended subtree.\n\nSuspense was originally shipped only for `React.lazy`. In React 18 it was\nextended to data fetching when paired with a **Suspense-compatible**\ndata source (e.g., frameworks that implement the promise-throwing protocol,\nor the new `React.use()` hook).\n\n**Rule of thumb:** Think of `\u003CSuspense>` like an error boundary but for\nloading states — place it wherever you want a loading fallback, not\nnecessarily right above every async component.\n",{"id":289,"difficulty":43,"q":354,"a":355},"What is the fallback prop on Suspense and when is it shown?","The **`fallback` prop** accepts any React node and is rendered whenever\nany child inside the `\u003CSuspense>` boundary is currently suspended (i.e.,\nwaiting for a lazy import or a data fetch to complete).\n\n```jsx\n\u003CSuspense fallback={\u003CSpinner size=\"lg\" \u002F>}>\n  \u003CUserProfile userId={id} \u002F>   {\u002F* may suspend while fetching *\u002F}\n\u003C\u002FSuspense>\n```\n\nKey timing rules:\n- The fallback appears **immediately** when a child suspends on the\n  initial render.\n- During a **transition** (`startTransition` \u002F `useTransition`), React\n  keeps showing the previous UI — not the fallback — until the new tree\n  is ready. This avoids unwanted spinners during navigations.\n- If a child suspends **after** the boundary already committed (e.g., a\n  nested update), React shows the fallback again only if the boundary\n  has not already resolved.\n\n`fallback` should be lightweight. A heavy fallback that itself needs data\ndefeats the purpose — keep it a skeleton, spinner, or placeholder.\n\n**Rule of thumb:** One Suspense boundary per independent loading region.\nNesting boundaries gives fine-grained control; a single top-level boundary\ngives a coarser \"page is loading\" experience.\n",{"id":357,"difficulty":34,"q":358,"a":359},"suspense-lazy-vs-data-fetching","What is the difference between using Suspense for lazy loading versus data fetching?","**Lazy loading** (`React.lazy`) uses Suspense to defer the download of a\nJavaScript bundle. It is fully supported in React 16.6+ and works in both\nlegacy and concurrent mode.\n\n**Data fetching** with Suspense requires the data source to implement the\n**throw-a-Promise** protocol, which React 18+ handles reliably in\nconcurrent mode. React itself does not fetch data — frameworks like\nNext.js, Relay, or SWR wire this up for you.\n\n```jsx\n\u002F\u002F 1. Lazy loading — supported everywhere\nconst Modal = lazy(() => import('.\u002FModal'))\n\n\u002F\u002F 2. Data fetching via React.use() — React 18+\nfunction Profile({ userPromise }) {\n  const user = use(userPromise)   \u002F\u002F throws the promise if pending\n  return \u003Ch1>{user.name}\u003C\u002Fh1>\n}\n\n\u002F\u002F Parent wraps both the same way\n\u003CSuspense fallback={\u003CSkeleton \u002F>}>\n  \u003CModal \u002F>          {\u002F* or *\u002F}\n  \u003CProfile userPromise={fetchUser(id)} \u002F>\n\u003C\u002FSuspense>\n```\n\nThe fallback mechanism is identical; the difference is what triggers the\nsuspend: a missing chunk vs. unresolved data.\n\n**Rule of thumb:** Use `React.lazy` for route-level code splitting today;\nuse data Suspense only through a framework or library that supports it —\nhand-rolling the throw-a-Promise protocol is fragile.\n",{"id":361,"difficulty":34,"q":362,"a":363},"suspense-compatible-data-source","What does \"Suspense-compatible\" data source mean?","A **Suspense-compatible** data source is any library or API that signals\n\"not ready yet\" by throwing a Promise when React tries to render a\ncomponent that needs its data. React catches the thrown Promise, shows the\nnearest Suspense fallback, and re-renders the component after the Promise\nresolves.\n\n```js\n\u002F\u002F Simplified read() that implements the protocol\nfunction createResource(promise) {\n  let status = 'pending', result\n  const suspender = promise.then(\n    data  => { status = 'success'; result = data },\n    error => { status = 'error';   result = error }\n  )\n  return {\n    read() {\n      if (status === 'pending')  throw suspender   \u002F\u002F suspend!\n      if (status === 'error')    throw result      \u002F\u002F bubble to error boundary\n      return result                                 \u002F\u002F return data when ready\n    }\n  }\n}\n```\n\nBuilt-in or framework-level Suspense-compatible sources in 2024:\n- `React.lazy` (code splitting)\n- `React.use(promise)` (React 18+)\n- Next.js App Router async Server Components\n- Relay's `useFragment` \u002F `useLazyLoadQuery`\n- SWR and React Query (experimental Suspense mode)\n\n**Rule of thumb:** Never throw a raw Promise from your own components —\nuse `React.use()` or a library. The protocol has subtle caching\nrequirements that are easy to get wrong.\n",{"id":365,"difficulty":34,"q":366,"a":367},"use-transition-explained","What does useTransition do and when should you use it?","**`useTransition`** returns `[isPending, startTransition]`. Wrapping a\nstate update inside `startTransition` tells React that the update is\n**non-urgent**: React can interrupt it, keep the current UI interactive,\nand yield to higher-priority work (e.g., typing) while computing the new\ntree.\n\n```jsx\nimport { useState, useTransition } from 'react'\n\nfunction SearchPage() {\n  const [query, setQuery]     = useState('')\n  const [results, setResults] = useState([])\n  const [isPending, startTransition] = useTransition()\n\n  function handleChange(e) {\n    setQuery(e.target.value)                 \u002F\u002F urgent — update input immediately\n    startTransition(() => {\n      setResults(heavyFilter(e.target.value)) \u002F\u002F non-urgent — can be deferred\n    })\n  }\n\n  return (\n    \u003C>\n      \u003Cinput value={query} onChange={handleChange} \u002F>\n      {isPending && \u003CSpinner \u002F>}\n      \u003CResultList items={results} \u002F>\n    \u003C\u002F>\n  )\n}\n```\n\n`isPending` is `true` while the transition render is in flight, letting\nyou show a subtle loading indicator without hiding the current content.\n\n**Rule of thumb:** Use `useTransition` when a state change triggers an\nexpensive re-render (e.g., filtering a large list, navigating to a heavy\nroute) and you want the UI to stay responsive during it.\n",{"id":369,"difficulty":43,"q":370,"a":371},"start-transition-function","What is startTransition and how does it differ from the useTransition hook?","**`startTransition`** is a standalone function imported directly from\nReact. It marks the state updates inside its callback as non-urgent\ntransitions, identical to the `startTransition` returned by\n`useTransition`.\n\n```jsx\nimport { startTransition } from 'react'\n\n\u002F\u002F Use the standalone import when you don't need isPending\nfunction handleTabChange(tab) {\n  startTransition(() => {\n    setActiveTab(tab)   \u002F\u002F non-urgent — React can interrupt if needed\n  })\n}\n```\n\nThe key difference:\n\n| | `startTransition` (import) | `useTransition` (hook) |\n|---|---|---|\n| Returns `isPending` | No | Yes |\n| Usable outside components | Yes | No (hook rules) |\n| Usable in event handlers | Yes | Yes |\n\nBoth produce the same concurrent behavior — the standalone version is\nsimply for cases where you do not need to reflect the pending state in UI,\nor when you are in a utility function outside a component.\n\n**Rule of thumb:** Prefer `useTransition` inside components so you can\nexpose `isPending` for a loading indicator. Use the standalone\n`startTransition` in router libraries, event utilities, or anywhere hooks\ncannot be called.\n",{"id":373,"difficulty":34,"q":374,"a":375},"use-deferred-value-explained","What does useDeferredValue do?","**`useDeferredValue`** accepts a value and returns a **deferred copy**\nthat lags behind the original during concurrent rendering. React renders\nthe urgent update with the latest value first, then re-renders with the\ndeferred value in the background when the browser is idle.\n\n```jsx\nimport { useState, useDeferredValue } from 'react'\n\nfunction FilteredList({ items }) {\n  const [filter, setFilter] = useState('')\n  const deferredFilter = useDeferredValue(filter)\n  \u002F\u002F deferredFilter may be one or more renders behind `filter`\n\n  const visible = items.filter(i =>\n    i.name.toLowerCase().includes(deferredFilter)\n  )\n\n  return (\n    \u003C>\n      \u003Cinput\n        value={filter}\n        onChange={e => setFilter(e.target.value)}  \u002F\u002F stays snappy\n      \u002F>\n      \u003CList items={visible} \u002F>   {\u002F* re-renders are deferred *\u002F}\n    \u003C\u002F>\n  )\n}\n```\n\nThe component that reads `deferredFilter` renders with the stale value\nwhile the urgent render (updating the input) commits first — the user\nsees no jank.\n\n**Rule of thumb:** Use `useDeferredValue` when you receive a value from a\nprop or context that you cannot control with `startTransition` — it lets\nyou defer a **derived** expensive render without touching where the value\noriginates.\n",{"id":377,"difficulty":72,"q":378,"a":379},"use-transition-vs-use-deferred-value","What is the difference between useTransition and useDeferredValue?","Both APIs defer work so React can keep the UI responsive, but they\noperate at different points in the data flow.\n\n**`useTransition`** wraps the **state setter** — you control which update\nis non-urgent at the source.\n\n**`useDeferredValue`** wraps the **value** downstream — you defer the\nexpensive consumption of a value you didn't produce (e.g., a prop from a\nparent or a context value).\n\n```jsx\n\u002F\u002F useTransition — you own the state setter\nconst [isPending, startTransition] = useTransition()\nstartTransition(() => setItems(newItems))   \u002F\u002F mark update as non-urgent\n\n\u002F\u002F useDeferredValue — someone else sets the state; you defer reading it\nfunction ExpensiveChild({ query }) {         \u002F\u002F query comes from a parent\n  const deferredQuery = useDeferredValue(query)\n  const results = heavySearch(deferredQuery) \u002F\u002F runs behind the scenes\n  return \u003CList items={results} \u002F>\n}\n```\n\n| | `useTransition` | `useDeferredValue` |\n|---|---|---|\n| Controls | the state update | the consumed value |\n| Exposes `isPending` | Yes | No |\n| Use when | you own the setter | the value comes from outside |\n\n**Rule of thumb:** Reach for `useTransition` first — it is more explicit.\nFall back to `useDeferredValue` when the value originates in a parent you\ncannot modify.\n",{"id":381,"difficulty":72,"q":382,"a":383},"tearing-concurrent-rendering","What is \"tearing\" in concurrent rendering and how does React prevent it?","**Tearing** is a visual inconsistency where different parts of the UI\nread the same external store at different points in time during a\nconcurrent render, producing a UI where some components show an old value\nand others show a new value simultaneously.\n\n```js\n\u002F\u002F External mutable store (not React state)\nlet externalColor = 'blue'\n\nfunction A() { return \u003Cdiv style={{ color: externalColor }} \u002F> }\nfunction B() { return \u003Cdiv style={{ color: externalColor }} \u002F> }\n\u002F\u002F If externalColor changes to 'red' mid-render:\n\u002F\u002F A → reads 'blue', B → reads 'red' → tearing\n```\n\nReact prevents tearing for **React-managed state** automatically because\nconcurrent renders compute a snapshot and commit it atomically. However,\n**external stores** (Redux, Zustand, Jotai, custom globals) can still\ntear if they are not integrated correctly.\n\nReact 18 introduced the **`useSyncExternalStore`** hook to fix this. It\nforces the store subscription to produce a synchronous, consistent\nsnapshot that React can safely read without tearing.\n\n```jsx\nimport { useSyncExternalStore } from 'react'\n\nconst color = useSyncExternalStore(\n  store.subscribe,          \u002F\u002F subscribe fn\n  store.getSnapshot,        \u002F\u002F synchronous snapshot\n  store.getServerSnapshot   \u002F\u002F SSR snapshot\n)\n```\n\n**Rule of thumb:** If you integrate a custom external store with\nconcurrent React, always use `useSyncExternalStore` — never read from\na mutable global directly inside a render.\n",{"id":385,"difficulty":34,"q":386,"a":387},"create-root-vs-legacy-render","What is the difference between ReactDOM.createRoot and the legacy ReactDOM.render?","**`ReactDOM.createRoot`** is the React 18 API that enables **concurrent\nmode**. The legacy `ReactDOM.render` runs React in **legacy (blocking)\nmode** where every render is synchronous and cannot be interrupted.\n\n```jsx\n\u002F\u002F Legacy mode — React 17 and below (or React 18 opt-out)\nimport ReactDOM from 'react-dom'\nReactDOM.render(\u003CApp \u002F>, document.getElementById('root'))\n\n\u002F\u002F Concurrent mode — React 18+\nimport { createRoot } from 'react-dom\u002Fclient'\nconst root = createRoot(document.getElementById('root'))\nroot.render(\u003CApp \u002F>)\n```\n\nKey differences:\n\n| | `ReactDOM.render` | `createRoot` |\n|---|---|---|\n| Rendering mode | Synchronous \u002F blocking | Concurrent (interruptible) |\n| `useTransition` works | No | Yes |\n| Automatic batching | Only inside event handlers | All state updates |\n| Suspense for data | Limited | Full |\n\nSwitching to `createRoot` is the single change needed to opt the entire\napp into React 18 concurrent features. Third-party libraries may need\nupdates to be concurrent-safe.\n\n**Rule of thumb:** All new React 18 apps should use `createRoot`. Migrate\nexisting apps by searching for `ReactDOM.render` — it is the single\nswitch that unlocks every concurrent feature.\n",{"id":389,"difficulty":72,"q":390,"a":391},"concurrent-rendering-interruptibility","What does it mean that concurrent rendering is \"interruptible\"?","In **legacy (blocking) mode**, once React starts rendering a tree it runs\nsynchronously to completion — no other JS can run until it finishes. Large\nrenders block the main thread and cause jank.\n\nIn **concurrent mode**, React renders in small chunks and regularly\n**yields** control back to the browser between chunks. If a\nhigher-priority update arrives (e.g., a keypress), React **discards** the\nin-progress lower-priority render, handles the urgent work, and then\n**restarts** the lower-priority render from scratch.\n\n```jsx\n\u002F\u002F React can pause this expensive render mid-way\n\u002F\u002F if the user types into an input while it is running\nstartTransition(() => {\n  setItems(filterLargeDataset(query))  \u002F\u002F low priority\n})\n\n\u002F\u002F Keypress → React interrupts the transition render,\n\u002F\u002F updates the input (high priority), then resumes\u002Frestarts\n\u002F\u002F the filterLargeDataset render\n```\n\nThis is why **render functions must be pure and side-effect free** —\nReact may call them multiple times for the same update. Effects (`useEffect`,\n`useLayoutEffect`) still run only once per committed render.\n\n**Rule of thumb:** Treat concurrent rendering as an implementation detail\n— write pure render functions and put side effects in `useEffect`, and\nReact's scheduler handles the rest.\n",{"id":393,"difficulty":34,"q":394,"a":395},"suspense-boundary-nesting","How does nesting Suspense boundaries work and why would you nest them?","You can nest `\u003CSuspense>` boundaries to give **independent loading states**\nto different regions of the UI. React resolves each boundary\nindependently — an outer boundary does not wait for an inner one to\nfinish before showing its content.\n\n```jsx\n\u003CSuspense fallback={\u003CPageSkeleton \u002F>}>        {\u002F* outer *\u002F}\n  \u003CHeader \u002F>\n\n  \u003CSuspense fallback={\u003CSidebarSkeleton \u002F>}>   {\u002F* inner — resolves independently *\u002F}\n    \u003CSidebar \u002F>\n  \u003C\u002FSuspense>\n\n  \u003CSuspense fallback={\u003CMainSkeleton \u002F>}>      {\u002F* inner *\u002F}\n    \u003CMainContent \u002F>\n  \u003C\u002FSuspense>\n\u003C\u002FSuspense>\n```\n\nReact walks up to the nearest boundary when a component suspends:\n- `\u003CSidebar>` suspending shows `\u003CSidebarSkeleton>` — not `\u003CPageSkeleton>`.\n- If `\u003CMainContent>` suspends before `\u003CSidebar>` has resolved, both inner\n  boundaries independently show their fallbacks.\n- The outer boundary only activates if no inner boundary catches the suspend.\n\nNesting boundaries lets the rest of the page stay visible and interactive\nwhile one slow section loads, improving perceived performance.\n\n**Rule of thumb:** Nest Suspense boundaries at each independently loading\nregion. Avoid a single top-level boundary if some data loads fast —\nit forces the whole page to show a spinner longer than necessary.\n",{"id":397,"difficulty":72,"q":398,"a":399},"nextjs-suspense-streaming","How does Next.js use Suspense for streaming SSR?","Next.js App Router renders **React Server Components** on the server and\nstreams the HTML to the browser in chunks via HTTP streaming (chunked\ntransfer encoding). Each `\u003CSuspense>` boundary in the tree acts as a\n**streaming flush point**.\n\n```jsx\n\u002F\u002F app\u002Fdashboard\u002Fpage.tsx (Next.js App Router)\nimport { Suspense } from 'react'\nimport { UserStats } from '.\u002FUserStats'   \u002F\u002F async Server Component\nimport { RecentOrders } from '.\u002FRecentOrders'\n\nexport default function Dashboard() {\n  return (\n    \u003Cmain>\n      \u003Ch1>Dashboard\u003C\u002Fh1>\n\n      \u003CSuspense fallback={\u003CStatsSkeleton \u002F>}>\n        \u003CUserStats \u002F>       {\u002F* Next.js streams this chunk when ready *\u002F}\n      \u003C\u002FSuspense>\n\n      \u003CSuspense fallback={\u003COrdersSkeleton \u002F>}>\n        \u003CRecentOrders \u002F>    {\u002F* streamed independently *\u002F}\n      \u003C\u002FSuspense>\n    \u003C\u002Fmain>\n  )\n}\n```\n\nThe shell HTML (layout, `\u003Ch1>`, skeletons) is sent immediately. As each\nasync Server Component resolves its `await`, Next.js flushes the rendered\nHTML chunk and a small `\u003Cscript>` that replaces the skeleton via React's\nselective hydration.\n\nThis means **Time to First Byte (TTFB)** is fast and the page is\nprogressively useful rather than blank until all data is ready.\n\n**Rule of thumb:** In Next.js App Router, wrap every slow data-fetching\nServer Component in its own `\u003CSuspense>` boundary with a skeleton fallback\n— the streaming benefit is lost if you have no boundaries.\n",{"id":401,"difficulty":72,"q":402,"a":403},"render-as-you-fetch-pattern","What is the \"render-as-you-fetch\" pattern and how does it differ from \"fetch-on-render\"?","**Fetch-on-render** (the traditional pattern) starts fetching inside\n`useEffect` after the component first renders. The component renders a\nloading state, mounts, fires the effect, data arrives, and the component\nre-renders — a **waterfall**.\n\n**Render-as-you-fetch** kicks off the fetch *before* rendering the\ncomponent — at route load time or in an event handler — and passes the\nin-flight Promise into the component. The component immediately reads\n(and suspends on) that Promise.\n\n```jsx\n\u002F\u002F FETCH-ON-RENDER (waterfall)\nfunction Profile() {\n  const [user, setUser] = useState(null)\n  useEffect(() => { fetchUser(id).then(setUser) }, [id])\n  if (!user) return \u003CSpinner \u002F>\n  return \u003Ch1>{user.name}\u003C\u002Fh1>\n}\n\n\u002F\u002F RENDER-AS-YOU-FETCH (no waterfall)\n\u002F\u002F 1. Start fetch when the user clicks the nav link\nlet userPromise = fetchUser(id)     \u002F\u002F fire immediately\n\n\u002F\u002F 2. Pass the promise to the component\nfunction Profile() {\n  const user = use(userPromise)     \u002F\u002F suspends until resolved\n  return \u003Ch1>{user.name}\u003C\u002Fh1>      \u002F\u002F no loading state needed here\n}\n\n\u002F\u002F 3. Wrap with Suspense in the parent\n\u003CSuspense fallback={\u003CSpinner \u002F>}>\n  \u003CProfile \u002F>\n\u003C\u002FSuspense>\n```\n\nRender-as-you-fetch eliminates the round-trip delay between mount and\nfetch start, and removes the need for manual loading-state bookkeeping.\n\n**Rule of thumb:** Prefer render-as-you-fetch for critical path data.\nFrameworks (Next.js, Remix) implement this automatically — adopt the\npattern manually only when fine-tuning client-side navigation.\n",{"id":405,"difficulty":72,"q":406,"a":407},"react-use-hook","What is the React.use() hook and how does it integrate with Suspense?","**`React.use()`** (stable in React 19, available as experimental in React\n18.3) is a hook that reads the value of a **Promise** or **Context**\ninside a component. When passed a pending Promise it throws it, triggering\nthe nearest Suspense boundary — this is the official, built-in way to\nimplement data Suspense without a library.\n\n```jsx\nimport { use, Suspense } from 'react'\n\nasync function fetchUser(id) {\n  const res = await fetch(`\u002Fapi\u002Fusers\u002F${id}`)\n  return res.json()\n}\n\n\u002F\u002F Start the fetch outside the component (render-as-you-fetch)\nconst userPromise = fetchUser(42)\n\nfunction UserCard() {\n  const user = use(userPromise)   \u002F\u002F suspends if pending, throws if rejected\n  return \u003Cp>{user.name}\u003C\u002Fp>\n}\n\nfunction App() {\n  return (\n    \u003CSuspense fallback={\u003Cp>Loading…\u003C\u002Fp>}>\n      \u003CUserCard \u002F>\n    \u003C\u002FSuspense>\n  )\n}\n```\n\nKey properties of `React.use()`:\n- Can be called **conditionally** (unlike other hooks).\n- Also accepts a **Context** object, replacing `useContext` when you need\n  conditional context reading.\n- A rejected Promise surfaces to the nearest **error boundary**, not\n  Suspense.\n\n**Rule of thumb:** Use `React.use(promise)` as the standard way to\nconsume async data in React 19+ components. For React 18 production apps,\nuse a library (SWR, React Query, Relay) that implements the same protocol\nwith caching.\n",{"description":32},"React Suspense and concurrent rendering interview questions — Suspense for data fetching, useTransition, useDeferredValue, startTransition, concurrent features, and React 18 rendering model.","react\u002Frendering-and-performance\u002Fsuspense-concurrent","Suspense and Concurrent Rendering","5FoMC1skNjK9widfvdQ--7FtGg1doJ4c-RwdTDNs7T8",1782244096651]