[{"data":1,"prerenderedAt":3342},["ShallowReactive",2],{"hub-react":3},{"framework":4,"topics":15,"qa":86},{"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",[16,24,32,41,50,59,68,77],{"id":17,"description":18,"extension":7,"frameworkSlug":8,"meta":19,"name":20,"order":13,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Freact-hooks.yml","useState, useEffect and the rules of hooks — the foundation of modern function-component React.",{},"Hooks","hooks","topics\u002Freact-hooks","jrLwNTdmn0GZbYOHuDjmphKf7fd5vx_B_TYSEX9Us1w",{"id":25,"description":26,"extension":7,"frameworkSlug":8,"meta":27,"name":28,"order":11,"slug":29,"stem":30,"__hash__":31},"topics\u002Ftopics\u002Freact-components.yml","JSX syntax, props, event handling, conditional rendering, and lists — the building blocks of every React UI.",{},"Components","components","topics\u002Freact-components","H4Z8kPxOMQnuMfHulzl4Rb9l7Vu6AndqtV3k7T-p4jE",{"id":33,"description":34,"extension":7,"frameworkSlug":8,"meta":35,"name":36,"order":37,"slug":38,"stem":39,"__hash__":40},"topics\u002Ftopics\u002Freact-state-and-data-flow.yml","Lifting state, the Context API, controlled vs. uncontrolled components, and solutions to prop drilling — how data moves through a React application.",{},"State and Data Flow",3,"state-and-data-flow","topics\u002Freact-state-and-data-flow","rig9Wq4Cttex5N-dzRNa_G5der7uowUIHS6BONtnkbw",{"id":42,"description":43,"extension":7,"frameworkSlug":8,"meta":44,"name":45,"order":46,"slug":47,"stem":48,"__hash__":49},"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",{"id":51,"description":52,"extension":7,"frameworkSlug":8,"meta":53,"name":54,"order":55,"slug":56,"stem":57,"__hash__":58},"topics\u002Ftopics\u002Freact-routing.yml","React Router v6 fundamentals, dynamic and nested routes, navigation hooks (useNavigate, useParams, useLocation), and protecting routes with auth guards.",{},"Routing",5,"routing","topics\u002Freact-routing","uQtCZwNBL9038tX4cckiGrc-kZEhtoaYkz3lhgW1jU0",{"id":60,"description":61,"extension":7,"frameworkSlug":8,"meta":62,"name":63,"order":64,"slug":65,"stem":66,"__hash__":67},"topics\u002Ftopics\u002Freact-state-management.yml","Redux Toolkit, Zustand, Context vs Redux trade-offs, and async state management with React Query — patterns for managing global and server state in production React apps.",{},"State Management",6,"state-management","topics\u002Freact-state-management","Gq8fJKVnohBmu5ktN9efTFFpmmSJb41he3EQuRxja3w",{"id":69,"description":70,"extension":7,"frameworkSlug":8,"meta":71,"name":72,"order":73,"slug":74,"stem":75,"__hash__":76},"topics\u002Ftopics\u002Freact-patterns.yml","Compound components, render props, HOCs, error boundaries, portals, refs, forwardRef, and useImperativeHandle — React advanced composition patterns for building flexible, reusable, and production-grade component APIs.",{},"Patterns",7,"patterns","topics\u002Freact-patterns","KARW213p_6I5xERIsw65KJCRrzpUp6VCKogaXYue-eM",{"id":78,"description":79,"extension":7,"frameworkSlug":8,"meta":80,"name":81,"order":82,"slug":83,"stem":84,"__hash__":85},"topics\u002Ftopics\u002Freact-testing.yml","React Testing Library, component interaction testing, mocking async operations, and testing custom hooks — everything you need to write confident, maintainable tests for React applications.",{},"Testing",8,"testing","topics\u002Freact-testing","3ziCNjEbki2ecb-F-Es314NCtEkSgqlzjnjqmkmE0eM",[87,176,330,422,499,594,674,764,843,923,1079,1170,1243,1337,1413,1500,1574,1657,1732,1826,1902,1995,2066,2152,2219,2294,2373,2468,2544,2637,2709,2804,2870,2958,3031,3126,3201,3272],{"id":88,"title":89,"body":90,"description":94,"difficulty":96,"extension":97,"framework":10,"frameworkSlug":8,"meta":98,"navigation":100,"order":13,"path":101,"questions":102,"questionsCount":168,"related":169,"seo":170,"seoDescription":171,"stem":172,"subtopic":173,"topic":28,"topicSlug":29,"updated":174,"__hash__":175},"qa\u002Freact\u002Fcomponents\u002Fjsx-rendering.md","Jsx Rendering",{"type":91,"value":92,"toc":93},"minimark",[],{"title":94,"searchDepth":11,"depth":11,"links":95},"",[],"easy","md",{"subtopicSlug":99},"jsx-rendering",true,"\u002Freact\u002Fcomponents\u002Fjsx-rendering",[103,107,111,115,119,124,128,132,136,140,144,148,152,156,160,164],{"id":104,"difficulty":96,"q":105,"a":106},"what-is-jsx","What is JSX and what does React do with it?","**JSX** is a syntax extension to JavaScript that lets you write HTML-like\nmarkup inside a `.js` or `.tsx` file. It is **not** valid JavaScript — a\nbuild tool (Babel, SWC, or the TypeScript compiler) transforms every JSX\nexpression into a `React.createElement` call before the browser sees it.\n\n```jsx\n\u002F\u002F What you write\nconst el = \u003Ch1 className=\"title\">Hello\u003C\u002Fh1>\n\n\u002F\u002F What the compiler produces (React 17+ JSX transform uses _jsx instead)\nconst el = React.createElement('h1', { className: 'title' }, 'Hello')\n```\n\nThe output is a plain JavaScript object — a **React element** — that\ndescribes what should appear on screen. React's renderer later turns that\ndescription into actual DOM nodes.\n\n**Rule of thumb:** JSX is syntactic sugar for `React.createElement`. If you\ncan't picture the compiled form, you don't yet fully understand what JSX is.\n",{"id":108,"difficulty":96,"q":109,"a":110},"jsx-single-root","Why must JSX always return a single root element?","Because JSX compiles to a single `React.createElement` call, which returns\none value. You can't return two values from one expression in JavaScript.\n\n```jsx\n\u002F\u002F ❌ Invalid — two adjacent elements at the top level\nreturn (\n  \u003Ch1>Title\u003C\u002Fh1>\n  \u003Cp>Body\u003C\u002Fp>\n)\n\n\u002F\u002F ✅ Wrap in a div\nreturn (\n  \u003Cdiv>\n    \u003Ch1>Title\u003C\u002Fh1>\n    \u003Cp>Body\u003C\u002Fp>\n  \u003C\u002Fdiv>\n)\n\n\u002F\u002F ✅ Or use a Fragment (no extra DOM node)\nreturn (\n  \u003C>\n    \u003Ch1>Title\u003C\u002Fh1>\n    \u003Cp>Body\u003C\u002Fp>\n  \u003C\u002F>\n)\n```\n\n**Rule of thumb:** when you need siblings without a wrapper DOM node, reach\nfor a Fragment (`\u003C>…\u003C\u002F>` or `\u003CReact.Fragment>…\u003C\u002FReact.Fragment>`).\n",{"id":112,"difficulty":96,"q":113,"a":114},"fragments","What are React Fragments and when should you use them?","A **Fragment** groups children without adding an extra node to the DOM.\nThere are two syntaxes:\n\n```jsx\n\u002F\u002F Short syntax — most common\nreturn (\n  \u003C>\n    \u003Cdt>Term\u003C\u002Fdt>\n    \u003Cdd>Definition\u003C\u002Fdd>\n  \u003C\u002F>\n)\n\n\u002F\u002F Long syntax — required when you need the key prop (e.g. in a list)\nreturn items.map(item => (\n  \u003CReact.Fragment key={item.id}>\n    \u003Cdt>{item.term}\u003C\u002Fdt>\n    \u003Cdd>{item.def}\u003C\u002Fdd>\n  \u003C\u002FReact.Fragment>\n))\n```\n\nUse Fragments when:\n- A wrapper `\u003Cdiv>` would break CSS layout (e.g. inside a `\u003Ctable>` or\n  flex\u002Fgrid container).\n- You're mapping over pairs of sibling elements that each need a `key`.\n\n**Rule of thumb:** prefer `\u003C>…\u003C\u002F>` by default; switch to the named form\nonly when you need to attach `key`.\n",{"id":116,"difficulty":96,"q":117,"a":118},"jsx-expressions","How do you embed JavaScript expressions in JSX?","Wrap any JavaScript **expression** in `{}`. An expression is anything that\nproduces a single value — a variable, function call, ternary, template\nliteral, etc.\n\n```jsx\nconst name = 'Alice'\nconst isAdmin = true\n\nreturn (\n  \u003Cdiv>\n    \u003Cp>Hello, {name.toUpperCase()}\u003C\u002Fp>\n    \u003Cp>Role: {isAdmin ? 'Admin' : 'User'}\u003C\u002Fp>\n    \u003Cp>Sum: {2 + 2}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n)\n```\n\n**What you cannot put in `{}`:**\n- Statements (`if`, `for`, variable declarations) — they don't produce a\n  value. Extract them above the return, or use expressions like ternaries\n  and `Array.map`.\n- Objects as children: `{myObject}` will throw; use `JSON.stringify` or\n  extract individual properties.\n\n**Rule of thumb:** if it can go on the right-hand side of `=`, it can go\ninside `{}`.\n",{"id":120,"difficulty":121,"q":122,"a":123},"jsx-vs-html-differences","medium","What are the key differences between JSX and HTML?","Several attribute names and rules differ:\n\n| HTML | JSX | Reason |\n|------|-----|--------|\n| `class` | `className` | `class` is a reserved JS keyword |\n| `for` | `htmlFor` | `for` is a reserved JS keyword |\n| `onclick` | `onClick` | Events use camelCase in JSX |\n| `\u003Cbr>` | `\u003Cbr \u002F>` | All tags must be closed in JSX |\n| `style=\"color:red\"` | `style={{ color: 'red' }}` | Style takes a JS object |\n| `tabindex` | `tabIndex` | camelCase for multi-word attributes |\n\n```jsx\n\u002F\u002F HTML\n\u002F\u002F \u003Clabel class=\"label\" for=\"email\" onclick=\"submit()\">Email\u003C\u002Flabel>\n\n\u002F\u002F JSX\n\u003Clabel className=\"label\" htmlFor=\"email\" onClick={submit}>Email\u003C\u002Flabel>\n```\n\nAdditionally, JSX is case-sensitive: `\u003Cdiv>` is a DOM element; `\u003CDiv>`\nwould be treated as a custom component.\n\n**Rule of thumb:** when an HTML attribute doesn't work in JSX, check if\nit's a reserved keyword or needs camelCase.\n",{"id":125,"difficulty":121,"q":126,"a":127},"boolean-attributes","How does JSX handle boolean attributes?","In JSX, omitting a value for an attribute is the same as setting it to\n`true`. You never write `disabled=\"true\"` — that's an HTML string, not a\nboolean.\n\n```jsx\n\u002F\u002F These are equivalent\n\u003Cinput disabled={true} \u002F>\n\u003Cinput disabled \u002F>\n\n\u002F\u002F Explicitly false — attribute is omitted from the DOM\n\u003Cinput disabled={false} \u002F>\n\n\u002F\u002F Dynamic\n\u003Cbutton disabled={isLoading}>Submit\u003C\u002Fbutton>\n```\n\nA common mistake is `disabled=\"false\"` — HTML treats any non-empty string\nas truthy, so the button would still be disabled. Always use `{false}`.\n\n**Rule of thumb:** use `{true}` \u002F `{false}` for any attribute that has a\nboolean semantic; never pass boolean values as strings.\n",{"id":129,"difficulty":121,"q":130,"a":131},"uppercase-component-names","Why must custom component names start with an uppercase letter in JSX?","JSX uses the case of the first letter to decide how to compile the tag:\n\n- **Lowercase** (`\u003Cdiv>`, `\u003Cspan>`) → `React.createElement('div', …)` —\n  passed as a **string**, rendered as a native DOM element.\n- **Uppercase** (`\u003CMyComponent>`) → `React.createElement(MyComponent, …)` —\n  passed as a **variable reference**, rendered as a React component.\n\n```jsx\n\u002F\u002F React treats 'button' as a DOM element — works as expected\nconst el = \u003Cbutton onClick={handle}>Click\u003C\u002Fbutton>\n\n\u002F\u002F React looks up `button` as a variable — ReferenceError if not defined\nconst el = \u003Cbutton \u002F>   \u002F\u002F ← lowercase: treated as DOM tag, fine\nconst el = \u003CButton \u002F>   \u002F\u002F ← uppercase: looks for a `Button` variable\n```\n\nIf you store a component in a lowercase variable, you must reassign it to\nan uppercase one before using it in JSX:\n\n```jsx\nconst components = { circle: CircleIcon }\nconst Icon = components['circle']   \u002F\u002F uppercase alias required\nreturn \u003CIcon \u002F>\n```\n\n**Rule of thumb:** always capitalize component names; use lowercase only\nfor HTML intrinsic elements.\n",{"id":133,"difficulty":96,"q":134,"a":135},"style-in-jsx","How do you apply inline styles in JSX?","The `style` prop takes a **JavaScript object**, not a CSS string. Property\nnames are camelCase versions of their CSS counterparts.\n\n```jsx\n\u002F\u002F ❌ HTML-style string — not valid in JSX\n\u003Cdiv style=\"color: red; font-size: 16px\">…\u003C\u002Fdiv>\n\n\u002F\u002F ✅ JSX object — note the double braces: outer {} for expression,\n\u002F\u002F    inner {} for the object literal\n\u003Cdiv style={{ color: 'red', fontSize: '16px', marginTop: 8 }}>…\u003C\u002Fdiv>\n```\n\nValues that are numbers (like `marginTop: 8`) are automatically converted\nto pixels for properties that accept pixel units. Values for others (like\n`opacity: 0.5`) are used as-is.\n\n**Rule of thumb:** for anything beyond a quick prototype, prefer CSS\nmodules or a utility class over inline styles — they don't support media\nqueries or pseudo-selectors.\n",{"id":137,"difficulty":96,"q":138,"a":139},"jsx-comments","How do you write comments in JSX?","Comments inside JSX must be wrapped in `{}` and use the `\u002F* … *\u002F` syntax —\nnot `\u002F\u002F`, which would comment out the closing `}` as well.\n\n```jsx\nreturn (\n  \u003Cdiv>\n    {\u002F* This is a JSX comment — it won't appear in the rendered output *\u002F}\n    \u003Cp>Visible text\u003C\u002Fp>\n    {\u002F* \u003Cp>Commented-out element\u003C\u002Fp> *\u002F}\n  \u003C\u002Fdiv>\n)\n```\n\nOutside of JSX (above the `return`), normal JS comments work fine.\n\n**Rule of thumb:** `{\u002F* comment *\u002F}` inside JSX, `\u002F\u002F comment` outside.\n",{"id":141,"difficulty":121,"q":142,"a":143},"jsx-spread-attributes","How do you spread an object's properties onto a JSX element?","Use the spread syntax `{...obj}` to forward a set of props without listing\neach one individually.\n\n```jsx\nconst buttonProps = { type: 'button', disabled: false, 'aria-label': 'Save' }\nreturn \u003Cbutton {...buttonProps}>Save\u003C\u002Fbutton>\n\n\u002F\u002F Equivalent to:\nreturn \u003Cbutton type=\"button\" disabled={false} aria-label=\"Save\">Save\u003C\u002Fbutton>\n```\n\nA common pattern in wrapper components:\n\n```jsx\nfunction IconButton({ icon, children, ...rest }) {\n  \u002F\u002F `rest` captures all props not explicitly destructured\n  return (\n    \u003Cbutton {...rest}>\n      \u003CIcon name={icon} \u002F>\n      {children}\n    \u003C\u002Fbutton>\n  )\n}\n```\n\n**Rule of thumb:** spread props enable flexible pass-through but make it\neasy to accidentally forward unknown props to DOM elements (which triggers\nReact warnings). Destructure known props and spread only the remainder.\n",{"id":145,"difficulty":96,"q":146,"a":147},"rendering-null","What happens when a component returns null?","Returning `null` renders **nothing** — the component mounts (lifecycle runs,\nrefs attach) but contributes no DOM nodes. It's the idiomatic way to\nconditionally hide output without unmounting.\n\n```jsx\nfunction Banner({ show, message }) {\n  if (!show) return null\n  return \u003Cdiv className=\"banner\">{message}\u003C\u002Fdiv>\n}\n```\n\nCrucially, returning `null` is different from not rendering the component\nat all:\n- `null` → component mounts; `useEffect` and refs still fire.\n- Conditional rendering (`{show && \u003CBanner \u002F>}`) → component is not mounted\n  when `show` is false; effects and state are destroyed.\n\n**Rule of thumb:** return `null` when you want the component in the tree\n(for effects or imperative refs) but invisible; use conditional rendering\nwhen you want it fully gone.\n",{"id":149,"difficulty":121,"q":150,"a":151},"rendering-arrays","How do you render an array of elements in JSX?","React can render an array of React elements as a child. The standard\napproach is `Array.map`:\n\n```jsx\nconst fruits = ['Apple', 'Banana', 'Cherry']\n\nreturn (\n  \u003Cul>\n    {fruits.map(fruit => (\n      \u003Cli key={fruit}>{fruit}\u003C\u002Fli>   \u002F\u002F key is required on the outermost element\n    ))}\n  \u003C\u002Ful>\n)\n```\n\nReact also accepts arrays returned directly:\n\n```jsx\nfunction Tags({ tags }) {\n  return tags.map(tag => \u003Cspan key={tag} className=\"tag\">{tag}\u003C\u002Fspan>)\n}\n```\n\nThings that React will **not** render as children: plain objects, `undefined`\n(silently skipped), and `false` (silently skipped — useful for short-circuits).\n\n**Rule of thumb:** always add a stable `key` to each element in a mapped\nlist; without it, React warns and may produce incorrect diffs.\n",{"id":153,"difficulty":121,"q":154,"a":155},"double-braces","What is the difference between single {} and double {{}} in JSX?","Single `{}` opens a JavaScript **expression slot** in JSX. Double `{{}}`\nis simply that expression slot containing an **object literal** — there's\nno special double-brace syntax.\n\n```jsx\n\u002F\u002F {} — expression slot\n\u003Cp>{count}\u003C\u002Fp>\n\n\u002F\u002F {{}} — expression slot containing an object literal\n\u003Cdiv style={{ color: 'red', fontSize: 16 }}>…\u003C\u002Fdiv>\n\u002F\u002F          ^outer: JSX expression  ^inner: JS object\n```\n\nThe confusion usually arises with `style` because it's the most common\nattribute that takes an object value.\n\n**Rule of thumb:** think of it as two separate things: \"open expression\"\nthen \"open object\". The outer `{}` is JSX; the inner `{}` is JavaScript.\n",{"id":157,"difficulty":96,"q":158,"a":159},"self-closing-tags","When must a JSX tag be self-closing?","In JSX, every element **must** be explicitly closed — either with a closing\ntag or with `\u002F>` for self-closing. This is stricter than HTML, where void\nelements like `\u003Cbr>` and `\u003Cimg>` don't require closing.\n\n```jsx\n\u002F\u002F ❌ HTML-style void element — invalid in JSX\n\u003Cinput type=\"text\">\n\u003Cimg src=\"photo.jpg\">\n\n\u002F\u002F ✅ Self-close when there are no children\n\u003Cinput type=\"text\" \u002F>\n\u003Cimg src=\"photo.jpg\" \u002F>\n\n\u002F\u002F ✅ Or use a closing tag (unusual for void elements, but valid JSX)\n\u003Cinput type=\"text\">\u003C\u002Finput>\n```\n\nCustom components can also be self-closed when you pass no children:\n`\u003CMyWidget \u002F>`.\n\n**Rule of thumb:** if an element has no children, self-close it with `\u002F>`;\nnever leave a tag unclosed in JSX.\n",{"id":161,"difficulty":121,"q":162,"a":163},"jsx-children-prop","How is the children prop passed and accessed in JSX?","Anything placed **between** opening and closing JSX tags is automatically\npassed as the `children` prop.\n\n```jsx\nfunction Card({ title, children }) {\n  return (\n    \u003Cdiv className=\"card\">\n      \u003Ch2>{title}\u003C\u002Fh2>\n      \u003Cdiv className=\"body\">{children}\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage — the \u003Cp> and \u003Cbutton> become children\n\u003CCard title=\"Welcome\">\n  \u003Cp>Hello there!\u003C\u002Fp>\n  \u003Cbutton>OK\u003C\u002Fbutton>\n\u003C\u002FCard>\n```\n\n`children` can be a string, a single element, an array of elements, or\neven a function (render prop pattern). Use `React.Children` utilities if\nyou need to iterate or count children programmatically.\n\n**Rule of thumb:** `children` is just a prop; you can also pass it\nexplicitly as `\u003CCard children={\u003Cp>Hi\u003C\u002Fp>} \u002F>`, though the nested form is\nmore readable.\n",{"id":165,"difficulty":121,"q":166,"a":167},"conditional-in-jsx","Can you use an if statement directly inside JSX? What should you do instead?","No — `if` is a statement, not an expression, so it can't go inside `{}`.\nJSX only accepts expressions.\n\n```jsx\n\u002F\u002F ❌ if statement inside JSX — syntax error\nreturn (\n  \u003Cdiv>\n    {if (isLoggedIn) { return \u003CDashboard \u002F> }}\n  \u003C\u002Fdiv>\n)\n\n\u002F\u002F ✅ Ternary — an expression\nreturn (\n  \u003Cdiv>\n    {isLoggedIn ? \u003CDashboard \u002F> : \u003CLogin \u002F>}\n  \u003C\u002Fdiv>\n)\n\n\u002F\u002F ✅ Logical && — renders right side only if left is truthy\nreturn (\n  \u003Cdiv>\n    {isAdmin && \u003CAdminPanel \u002F>}\n  \u003C\u002Fdiv>\n)\n\n\u002F\u002F ✅ If\u002Felse above the return — perfectly fine\nlet content\nif (isLoggedIn) { content = \u003CDashboard \u002F> }\nelse { content = \u003CLogin \u002F> }\nreturn \u003Cdiv>{content}\u003C\u002Fdiv>\n```\n\n**Rule of thumb:** for simple two-path branches use a ternary; for one-side\nrendering use `&&`; for complex logic extract it above the `return`.\n",16,null,{"description":94},"React JSX interview questions — JSX syntax, transpilation, fragments, expressions, boolean attributes, and the differences between JSX and HTML.","react\u002Fcomponents\u002Fjsx-rendering","JSX and Rendering","2026-06-23","yl8pAZz6eXYk_NZkXSaQAtjCi9t1s27W0dWeiEbz0pk",{"id":177,"title":178,"body":179,"description":94,"difficulty":96,"extension":97,"framework":10,"frameworkSlug":8,"meta":183,"navigation":100,"order":13,"path":184,"questions":185,"questionsCount":323,"related":169,"seo":324,"seoDescription":325,"stem":326,"subtopic":327,"topic":20,"topicSlug":21,"updated":328,"__hash__":329},"qa\u002Freact\u002Fhooks\u002Fusestate.md","Usestate",{"type":91,"value":180,"toc":181},[],{"title":94,"searchDepth":11,"depth":11,"links":182},[],{},"\u002Freact\u002Fhooks\u002Fusestate",[186,190,194,198,202,206,211,215,219,223,227,231,235,239,243,247,251,255,259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319],{"id":187,"difficulty":96,"q":188,"a":189},"what-is-usestate","What does useState return?","`useState` returns an array of exactly two elements: the **current state\nvalue** for this render, and a **setter function** that schedules an update\nand triggers a re-render. You destructure them, and the `[value, setValue]`\nnaming convention is just that — a convention, not something React enforces.\n\n```jsx\nconst [count, setCount] = useState(0)\n\u002F\u002F     ▲ current value  ▲ setter that re-renders with the next value\n```\n\nA few things that trip people up in interviews:\n\n- The argument (`0` here) is only the **initial** value. On every render\n  after the first, React ignores it and hands you the latest stored value.\n- The setter has a **stable identity** — React guarantees it never changes\n  between renders, so it's safe to omit from `useEffect`\u002F`useCallback`\n  dependency arrays.\n- Calling the setter does **not** mutate `count` in the current scope; it\n  asks React to render again with a new value.\n",{"id":191,"difficulty":121,"q":192,"a":193},"async-state","Why does state appear to be \"one render behind\"?","Because `count` is a **const captured by this render's closure**, not a live\nreference to a mutable box. Each render gets its own `count` constant frozen\nat the value it had when that render ran. Calling `setCount` schedules a\n*future* render with a new value — it cannot reach back and change the\n`count` you're currently looking at.\n\n```jsx\nfunction handleClick() {\n  console.log(count)   \u002F\u002F e.g. 0\n  setCount(count + 1)  \u002F\u002F schedules a render where count will be 1\n  console.log(count)   \u002F\u002F STILL 0 — same render, same frozen constant\n}\n```\n\nSo state isn't really \"behind\" — you're reading a snapshot. The new value\nbecomes visible only in the next render's function body. If you need the\nupdated value immediately after setting it, derive it locally\n(`const next = count + 1`) or read it in an effect that runs after the\nre-render.\n",{"id":195,"difficulty":121,"q":196,"a":197},"functional-update","When should you use a functional state update?","Use the functional form — `setCount(c => c + 1)` — whenever the next state is\n**derived from the previous state**, especially when several updates happen in\none event or across async boundaries. React passes the most up-to-date value\ninto your updater, so you never base a calculation on a stale snapshot.\n\n```jsx\n\u002F\u002F All three read the same stale `count`, so this adds 1, not 3\nsetCount(count + 1)\nsetCount(count + 1)\nsetCount(count + 1)\n\n\u002F\u002F Each updater receives the result of the previous one -> +3\nsetCount(c => c + 1)\nsetCount(c => c + 1)\nsetCount(c => c + 1)\n```\n\nRule of thumb: if the new value mentions the old value, prefer the function\nform. If you're setting an unrelated value (`setCount(0)`), the direct form\nis fine.\n",{"id":199,"difficulty":121,"q":200,"a":201},"lazy-init","What is lazy initial state?","If your initial value is expensive to compute, pass a **function** to\n`useState` instead of the value itself. React calls that function **only on\nthe first render** and ignores it afterwards. Passing the value directly would\nre-run the expensive computation on *every* render and then throw the result\naway.\n\n```jsx\n\u002F\u002F readFromLocalStorage() runs on every single render\nconst [items, setItems] = useState(readFromLocalStorage())\n\n\u002F\u002F runs once, on mount only\nconst [items, setItems] = useState(() => readFromLocalStorage())\n```\n\nWatch the distinction: `useState(expensive())` *calls* `expensive` every\nrender (its return value is the argument), whereas `useState(expensive)` \u002F\n`useState(() => expensive())` hands React the function to call once.\n",{"id":203,"difficulty":121,"q":204,"a":205},"object-state","How do you update an object in state?","Unlike `this.setState` in class components, the `useState` setter **replaces**\nthe value — it does not shallow-merge. To change one field of an object you\nmust spread the previous object yourself and override the field, returning a\n**new** object (mutating the existing one won't trigger a re-render because the\nreference is unchanged).\n\n```jsx\nconst [user, setUser] = useState({ name: 'Ada', age: 36 })\n\n\u002F\u002F new object, old fields preserved, name overridden\nsetUser(u => ({ ...u, name: 'Grace' }))\n\n\u002F\u002F mutates in place — same reference, React skips the re-render\nuser.name = 'Grace'\nsetUser(user)\n```\n\nFor deeply nested state this spreading gets verbose; that's often the signal\nto reach for `useReducer` or a state library.\n",{"id":207,"difficulty":208,"q":209,"a":210},"batching","hard","What is state batching?","Batching is React grouping multiple state updates into a **single re-render**\nfor performance, instead of re-rendering once per `setState` call. If you call\nthree setters in one click handler, React processes them together and renders\nonce.\n\n```jsx\nfunction handleClick() {\n  setA(1)\n  setB(2)\n  setC(3)\n  \u002F\u002F ONE re-render, not three\n}\n```\n\nBefore React 18, batching only happened inside React event handlers; updates\nin `setTimeout`, promises, or native event listeners each caused their own\nrender. **React 18's automatic batching** extends grouping to those async\ncontexts too. If you ever need to opt out and force a synchronous, separate\nrender, wrap the update in `flushSync` from `react-dom`.\n",{"id":212,"difficulty":121,"q":213,"a":214},"derived-state","Should you store computed\u002Fderived values in state?","Usually **no**. If a value can be calculated from existing props or state,\nderive it **during render** instead of duplicating it in `useState` — extra\nstate can drift out of sync and forces you to keep two things updated.\n\n```jsx\n\u002F\u002F redundant state that can desync\nconst [items, setItems] = useState([])\nconst [count, setCount] = useState(0)\n\n\u002F\u002F derive it\nconst [items, setItems] = useState([])\nconst count = items.length\n```\n\nOnly store something in state if it's genuinely independent input. \"You might\nnot need state\" is the React team's own guidance.\n",{"id":216,"difficulty":121,"q":217,"a":218},"lift-state-up","What does \"lifting state up\" mean?","When two components need to share or stay in sync over the same data, you move\nthe state to their **closest common parent** and pass it down as props plus a\nsetter callback. The parent becomes the single source of truth.\n\n```jsx\nfunction Parent() {\n  const [value, setValue] = useState('')\n  return (\n    \u003C>\n      \u003CInput value={value} onChange={setValue} \u002F>\n      \u003CPreview value={value} \u002F>\n    \u003C\u002F>\n  )\n}\n```\n\nIt's the standard fix for \"these siblings need the same data.\" When lifting gets\npainful across many levels, that's the signal to reach for Context or a store.\n",{"id":220,"difficulty":121,"q":221,"a":222},"usestate-vs-useref","When should you use useRef instead of useState?","Use `useState` for values that should **trigger a re-render** when they change.\nUse `useRef` for mutable values that should **persist across renders but NOT\ncause re-renders** — timer ids, previous values, DOM nodes.\n\n```jsx\nconst [count, setCount] = useState(0) \u002F\u002F UI depends on it -> re-render\nconst renders = useRef(0)             \u002F\u002F bookkeeping -> no re-render\nrenders.current++\n```\n\nChanging `ref.current` is invisible to React's render cycle. If the screen needs\nto reflect the value, it belongs in state; otherwise a ref avoids needless\nrenders.\n",{"id":224,"difficulty":208,"q":225,"a":226},"reset-state-key","How do you reset a component's state to its initial values?","The cleanest way is to change the component's **`key`**. React treats a new key\nas a brand-new component, unmounting the old instance (discarding its state) and\nmounting a fresh one — no manual reset code.\n\n```jsx\n\u003CProfile key={userId} userId={userId} \u002F>\n\u002F\u002F when userId changes, Profile remounts with fresh state\n```\n\nAlternatives are manually calling setters back to initial values, but `key` is\nidiomatic for \"start this subtree over.\" Avoid resetting state inside an effect\nby watching a prop — the `key` approach is simpler and bug-free.\n",{"id":228,"difficulty":208,"q":229,"a":230},"store-function-state","How do you store a function in state?","Because the `useState` setter treats a **function argument as an updater**, you\nmust wrap a function you want to *store* in another function — otherwise React\ncalls it instead of saving it.\n\n```jsx\n\u002F\u002F React invokes handleClick to compute the next state\nconst [fn, setFn] = useState(handleClick)\nsetFn(handleClick)\n\n\u002F\u002F wrap it so it's stored, not called\nconst [fn, setFn] = useState(() => handleClick)\nsetFn(() => handleClick)\n```\n\nThe same rule applies to lazy initialization. Storing functions in state is rare\n— usually a ref or just defining the function in render is cleaner.\n",{"id":232,"difficulty":96,"q":233,"a":234},"array-state-add","How do you add and remove items in array state?","Treat the array as **immutable** — never `push`\u002F`splice` the existing array\n(same reference -> no re-render). Build a new array with spread or `filter`.\n\n```jsx\n\u002F\u002F add\nsetItems(prev => [...prev, newItem])\n\u002F\u002F remove by id\nsetItems(prev => prev.filter(it => it.id !== id))\n\u002F\u002F insert at index\nsetItems(prev => [...prev.slice(0, i), newItem, ...prev.slice(i)])\n```\n\nUsing the functional updater (`prev =>`) keeps you correct when several updates\nbatch together.\n",{"id":236,"difficulty":121,"q":237,"a":238},"array-state-update","How do you update one object inside an array in state?","Map over the array, returning a **new object** for the matching item and the\noriginals for the rest. Mutating the found object in place won't re-render and\ncan corrupt previous renders.\n\n```jsx\nsetUsers(prev =>\n  prev.map(u => u.id === id ? { ...u, name: 'Ada' } : u)\n)\n```\n\nThe rule: new array **and** new object for whatever you change. For deeply\nnested updates this gets verbose — a signal to use `useReducer` or Immer.\n",{"id":240,"difficulty":208,"q":241,"a":242},"usestate-vs-usereducer","When should you choose useReducer over useState?","Reach for `useReducer` when state is **complex** (multiple sub-values that change\ntogether), when the **next state depends on intricate logic**, or when you want\nto **centralize update logic** in one tested function instead of scattering\nsetters.\n\n```jsx\nconst [state, dispatch] = useReducer(reducer, initial)\ndispatch({ type: 'increment', by: 2 })\n```\n\n`useState` is best for simple, independent values. A heuristic: if you find\nyourself calling several setters together or your setter logic is branchy, a\nreducer makes intent clearer and easier to test.\n",{"id":244,"difficulty":96,"q":245,"a":246},"controlled-input","How do you build a controlled input with useState?","A controlled input gets its `value` from state and updates state on every\nkeystroke via `onChange`, making React the single source of truth.\n\n```jsx\nconst [text, setText] = useState('')\n\u003Cinput value={text} onChange={e => setText(e.target.value)} \u002F>\n```\n\nForgetting `onChange` while setting `value` makes the field **read-only** (React\nwarns). The benefit is you can validate, format, or react to every change; the\ncost is a re-render per keystroke (rarely a problem).\n",{"id":248,"difficulty":121,"q":249,"a":250},"controlled-vs-uncontrolled","What is the difference between controlled and uncontrolled components?","- **Controlled** — React state holds the value (`value` + `onChange`). Predictable\n  and easy to validate, but re-renders on each change.\n- **Uncontrolled** — the DOM holds the value; you read it via a `ref` when needed\n  (`defaultValue` for the initial value).\n\n```jsx\n\u002F\u002F uncontrolled\nconst ref = useRef()\n\u003Cinput defaultValue=\"hi\" ref={ref} \u002F>\n\u002F\u002F read ref.current.value on submit\n```\n\nControlled is the default recommendation; uncontrolled suits simple forms,\nfile inputs, or integrating non-React widgets.\n",{"id":252,"difficulty":121,"q":253,"a":254},"multiple-vs-single-object","Should you use multiple state variables or one state object?","Prefer **multiple `useState` calls** for values that change independently — it's\nsimpler and you don't have to spread-merge on every update. Group into one object\nonly when fields genuinely change **together**.\n\n```jsx\n\u002F\u002F independent values\nconst [name, setName] = useState('')\nconst [age, setAge] = useState(0)\n\n\u002F\u002F grouping requires manual merge (no auto-merge like class setState)\nsetForm(prev => ({ ...prev, name: 'Ada' }))\n```\n\nRemember the `useState` setter **replaces**, it doesn't merge — so an object of\nstate means spreading the rest every time.\n",{"id":256,"difficulty":208,"q":257,"a":258},"nested-object-update","How do you update deeply nested state immutably?","Spread at **every** level you change, all the way down — only the touched\nbranches get new references.\n\n```jsx\nsetUser(prev => ({\n  ...prev,\n  address: { ...prev.address, city: 'Paris' },\n}))\n```\n\nThis is error-prone and verbose for deep trees. Options: restructure to flatter\nstate, switch to `useReducer`, or use **Immer** (`produce`) which lets you\n\"mutate\" a draft and produces the immutable update for you.\n",{"id":260,"difficulty":121,"q":261,"a":262},"avoid-redundant-state","What is redundant state and why avoid it?","Redundant state is data you keep in `useState` that's already derivable from\nother state or props. It invites bugs because you must remember to update it\neverywhere, and the copies can disagree.\n\n```jsx\n\u002F\u002F fullName must be kept in sync manually\nconst [fullName, setFullName] = useState('')\n\u002F\u002F derive it\nconst fullName = `${first} ${last}`\n```\n\nKeep state **minimal and orthogonal** — the smallest set of independent values\nfrom which everything else is computed during render.\n",{"id":264,"difficulty":96,"q":265,"a":266},"toggle-boolean","How do you toggle a boolean in state?","Use the functional updater so you always flip the **latest** value, which matters\nif toggles can batch.\n\n```jsx\nconst [open, setOpen] = useState(false)\nconst toggle = () => setOpen(o => !o)\n```\n\nAvoid `setOpen(!open)` in code paths that may run multiple times in one event —\nit reads a possibly stale `open`. The functional form is always safe.\n",{"id":268,"difficulty":208,"q":269,"a":270},"prev-in-async","How do you read the latest state inside an async callback?","A callback captures the state value from the render it was created in, so after\nan `await` it may be **stale**. To act on the latest value, use the functional\nupdater (which receives the current value) or store it in a ref.\n\n```jsx\nasync function save() {\n  await delay(1000)\n  \u002F\u002F `count` is whatever it was when save() was created\n  \u002F\u002F read the latest via the updater\n  setCount(latest => { send(latest); return latest })\n}\n```\n\nCleaner still: pass the needed value as an argument, or keep a `useRef` mirror of\nthe state for \"read latest\" access without triggering renders.\n",{"id":272,"difficulty":208,"q":273,"a":274},"set-bailout","Does setting state to the same value cause a re-render?","If you set state to a value that's **`Object.is`-equal** to the current one,\nReact **bails out** and skips re-rendering that component (it may still re-run\nthe component once to check, then stop).\n\n```jsx\nconst [n, setN] = useState(0)\nsetN(0) \u002F\u002F same value -> React bails out, no committed re-render\n```\n\nThe catch: this is **reference** equality. `setItems([])` with a *new* empty\narray is a different reference, so it **does** re-render even though the contents\nlook identical. Don't create fresh objects\u002Farrays for \"no change.\"\n",{"id":276,"difficulty":96,"q":277,"a":278},"state-vs-props","What is the difference between state and props?","- **State** is data a component **owns and can change** over time via its setter.\n- **Props** are data **passed in from a parent**; the child treats them as\n  **read-only**.\n\n```jsx\nfunction Counter({ step }) {        \u002F\u002F step is a prop (read-only)\n  const [count, setCount] = useState(0) \u002F\u002F count is state (owned)\n  return \u003Cbutton onClick={() => setCount(c => c + step)}>{count}\u003C\u002Fbutton>\n}\n```\n\nA child never mutates props; to change parent data it calls a callback prop. One\ncomponent's state is often another's props (passed down).\n",{"id":280,"difficulty":121,"q":281,"a":282},"rules-of-hooks","Why must useState be called at the top level, not in conditions?","React identifies each hook by its **call order**, not a name. Calling `useState`\ninside a condition, loop, or after an early return changes that order between\nrenders, so React mismatches state to the wrong hook.\n\n```jsx\n\u002F\u002F breaks hook ordering\nif (loggedIn) {\n  const [name, setName] = useState('')\n}\n\u002F\u002F always call unconditionally; branch on the value\nconst [name, setName] = useState('')\nif (loggedIn) { \u002F* use name *\u002F }\n```\n\nAlways call hooks at the top level of the component, in the same order every\nrender — the `eslint-plugin-react-hooks` rule enforces this.\n",{"id":284,"difficulty":121,"q":285,"a":286},"state-no-rerender","When does updating state not trigger a re-render?","Two common cases: (1) you **mutated** the existing object\u002Farray and passed the\nsame reference, so React sees no change; or (2) you set a primitive to the\n**same value** (React bails out via `Object.is`).\n\n```jsx\n\u002F\u002F mutate + same reference -> no re-render\nuser.name = 'Ada'\nsetUser(user)\n\u002F\u002F new reference\nsetUser({ ...user, name: 'Ada' })\n```\n\nThe fix is always to produce a **new reference** for changed objects\u002Farrays. If\nthe UI \"isn't updating,\" this mutation trap is the first thing to check.\n",{"id":288,"difficulty":208,"q":289,"a":290},"set-during-render","Can you call a state setter during render?","Generally you should not — setting state in the render body unconditionally\ncauses an **infinite loop**. React *does* support a narrow pattern: calling a\nsetter **conditionally during render** to adjust state based on a prop change,\nwhich it handles without an extra paint.\n\n```jsx\n\u002F\u002F rare, allowed: derive on prop change without an effect\nconst [prevId, setPrevId] = useState(id)\nif (id !== prevId) {\n  setPrevId(id)\n  setSelection(null) \u002F\u002F reset when id changes, no effect needed\n}\n```\n\nMost of the time you don't need this — prefer deriving values or the `key` reset\ntrick. Never call a setter unconditionally in render.\n",{"id":292,"difficulty":121,"q":293,"a":294},"setstate-loop","What causes an infinite re-render loop with useState?","Calling a setter **unconditionally during render**, or inside an effect whose\ndependencies you keep changing, makes React render -> set -> render forever.\n\n```jsx\n\u002F\u002F sets state every render -> infinite loop\nconst [n, setN] = useState(0)\nsetN(n + 1)\n\n\u002F\u002F effect with a new object dep each render\nuseEffect(() => setData(load()), [{}])\n```\n\nFixes: only set state in **event handlers** or **effects with stable deps**,\nderive values instead of storing them, and avoid fresh object\u002Farray literals in\ndependency arrays.\n",{"id":296,"difficulty":208,"q":297,"a":298},"state-from-props","Why doesn't state update when the prop used to initialize it changes?","The `useState` initializer is only read on the **first render**. Passing a prop\nas the initial value captures it once; later changes to the prop don't flow into\nthe state.\n\n```jsx\nfunction Field({ initial }) {\n  const [value, setValue] = useState(initial) \u002F\u002F only the first `initial` is used\n  \u002F\u002F later `initial` changes are ignored\n}\n```\n\nIf you truly need to reset when the prop changes, use the **`key`** prop to\nremount, or the conditional set-during-render pattern. Often the better answer is\nto not copy the prop into state at all.\n",{"id":300,"difficulty":121,"q":301,"a":302},"expensive-derived","How do you cache an expensive derived value without storing it in state?","Use `useMemo` — it recomputes the value only when its dependencies change, so you\nget the benefit of a cached derivation **without** the desync risk of putting it\nin state.\n\n```jsx\nconst sorted = useMemo(\n  () => [...items].sort(compare),\n  [items]\n)\n```\n\nThis keeps `items` as the single source of truth while avoiding re-sorting on\nevery keystroke. Don't reach for `useMemo` until the computation is actually\nexpensive — needless memoization adds its own overhead.\n",{"id":304,"difficulty":121,"q":305,"a":306},"state-colocation","Where should state live in a React component tree?","Keep state as **close as possible** to where it's used (colocation), and lift it\nonly as high as the nearest common ancestor that needs to share it. Over-lifting\nstate to the top causes unnecessary re-renders and prop drilling.\n\n```jsx\n\u002F\u002F a modal's \"open\" state belongs in the component that owns the modal,\n\u002F\u002F not in the app root\nfunction Toolbar() {\n  const [open, setOpen] = useState(false)\n  \u002F\u002F ...\n}\n```\n\nColocated state means fewer renders and simpler components; global\u002Fapp state\nshould be reserved for genuinely shared data.\n",{"id":308,"difficulty":121,"q":309,"a":310},"form-generic-handler","How do you manage many form fields with one state object?","Hold the fields in one object and use a generic change handler keyed by the\ninput's `name`, spreading the previous object to preserve the other fields.\n\n```jsx\nconst [form, setForm] = useState({ name: '', email: '' })\nconst onChange = e =>\n  setForm(prev => ({ ...prev, [e.target.name]: e.target.value }))\n\n\u003Cinput name=\"email\" value={form.email} onChange={onChange} \u002F>\n```\n\nThe computed key `[e.target.name]` updates just one field. For large\u002Fvalidated\nforms, a form library or `useReducer` scales better than hand-rolling this.\n",{"id":312,"difficulty":121,"q":313,"a":314},"no-setstate-callback","Is there a setState callback like in class components?","No. The class `this.setState(value, callback)` second argument doesn't exist for\nthe `useState` setter. To run code **after** a state update commits, use a\n`useEffect` that depends on that state.\n\n```jsx\nconst [count, setCount] = useState(0)\nuseEffect(() => {\n  \u002F\u002F runs after the render caused by count changing\n  analytics.track(count)\n}, [count])\n```\n\nThis is the hooks way to \"do X after state changes\" — react to the new value in\nan effect rather than passing a callback to the setter.\n",{"id":316,"difficulty":96,"q":317,"a":318},"number-input","How do you handle number inputs with useState?","An `\u003Cinput>`'s value is always a **string**, so convert it when you need a\nnumber, and decide how to handle empty\u002Finvalid input.\n\n```jsx\nconst [age, setAge] = useState('')\n\u003Cinput\n  type=\"number\"\n  value={age}\n  onChange={e => setAge(e.target.value)} \u002F\u002F keep the raw string in state\n\u002F>\nconst ageNum = age === '' ? 0 : Number(age) \u002F\u002F parse where you use it\n```\n\nStoring the raw string avoids fighting the input (e.g. a partially typed `-` or\n`.`), and you parse to a number only at the point of use.\n",{"id":320,"difficulty":208,"q":321,"a":322},"batched-puzzle","What does calling the same setter three times with the value form produce?","```jsx\nconst [count, setCount] = useState(0)\nfunction handle() {\n  setCount(count + 1)\n  setCount(count + 1)\n  setCount(count + 1)\n} \u002F\u002F after one click, count is 1 — not 3\n```\n\nAll three read the **same** `count` (0) from this render's closure, each\ncomputing `1`, and batching collapses them to a single update of `1`. Use the\nfunctional form to actually add 3:\n\n```jsx\nsetCount(c => c + 1) \u002F\u002F ×3 -> each receives the previous result -> 3\n```\n\nThis puzzle tests whether you understand closures-over-state plus batching.\n",34,{"description":94},"React useState interview questions — state updates, batching, functional updates, lazy initialization and why state seems one render behind.","react\u002Fhooks\u002Fusestate","useState","2026-06-17","Q98L5hE1YW-Y2GjlKdcwnfcyAh33LwxO1PXqqxYkUEo",{"id":331,"title":332,"body":333,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":337,"navigation":100,"order":13,"path":338,"questions":339,"questionsCount":416,"related":169,"seo":417,"seoDescription":418,"stem":419,"subtopic":332,"topic":72,"topicSlug":74,"updated":420,"__hash__":421},"qa\u002Freact\u002Fpatterns\u002Fcompound-components.md","Compound Components",{"type":91,"value":334,"toc":335},[],{"title":94,"searchDepth":11,"depth":11,"links":336},[],{},"\u002Freact\u002Fpatterns\u002Fcompound-components",[340,344,348,352,356,360,364,368,372,376,380,384,388,392,396,400,404,408,412],{"id":341,"difficulty":96,"q":342,"a":343},"what-are-compound-components","What are compound components and what problem do they solve?","**Compound components** are a set of components that work together to form a cohesive UI\nunit while sharing state implicitly. The parent component owns the state; child\nsub-components consume it without the consumer having to wire it up manually.\n\nThe problem they solve is **prop explosion** on monolithic components. Consider a `\u003CTabs>`\ncomponent — without the pattern you end up passing `tabs`, `activeTab`, `onTabChange`,\n`renderPanel`, and more as props. With compound components the API becomes declarative:\n\n```jsx\n\u002F\u002F Monolithic: consumer must juggle every prop\n\u003CTabs\n  tabs={[{ label: 'A', content: \u003Cdiv \u002F> }]}\n  activeTab={active}\n  onTabChange={setActive}\n\u002F>\n\n\u002F\u002F Compound: consumer composes sub-components freely\n\u003CTabs defaultValue=\"a\">\n  \u003CTabs.List>\n    \u003CTabs.Tab value=\"a\">Tab A\u003C\u002FTabs.Tab>\n    \u003CTabs.Tab value=\"b\">Tab B\u003C\u002FTabs.Tab>\n  \u003C\u002FTabs.List>\n  \u003CTabs.Panel value=\"a\">Content A\u003C\u002FTabs.Panel>\n  \u003CTabs.Panel value=\"b\">Content B\u003C\u002FTabs.Panel>\n\u003C\u002FTabs>\n```\n\n**Rule of thumb:** Reach for compound components when multiple tightly related pieces of\nUI need to share state but the caller should control their layout and composition.\n",{"id":345,"difficulty":96,"q":346,"a":347},"context-based-state-sharing","How do compound sub-components share state without prop drilling?","The standard approach is a **dedicated Context** created inside the compound component\nmodule. The parent component provides the shared state; sub-components consume it via\n`useContext`. This keeps the shared state invisible to the consumer of the component.\n\n```jsx\n\u002F\u002F 1. Create context (not exported — internal implementation detail)\nconst TabsContext = createContext(null);\n\n\u002F\u002F 2. Parent provides state\nfunction Tabs({ defaultValue, children }) {\n  const [active, setActive] = useState(defaultValue);\n  return (\n    \u003CTabsContext.Provider value={{ active, setActive }}>\n      {children}\n    \u003C\u002FTabsContext.Provider>\n  );\n}\n\n\u002F\u002F 3. Sub-components consume without any extra props from caller\nfunction TabsTab({ value, children }) {\n  const { active, setActive } = useContext(TabsContext);\n  return (\n    \u003Cbutton\n      aria-selected={active === value}\n      onClick={() => setActive(value)}\n    >\n      {children}\n    \u003C\u002Fbutton>\n  );\n}\n```\n\n**Rule of thumb:** Keep the Context object private to the module; export only the\nsub-components so consumers never reach into internal state directly.\n",{"id":349,"difficulty":96,"q":350,"a":351},"dot-notation-api","What is the dot-notation API pattern and how do you set it up?","**Dot-notation** attaches sub-components as static properties on the parent component\n(`Menu.Item`, `Tabs.Panel`). This groups related components under a single import and\nmakes the relationship obvious in JSX.\n\n```jsx\n\u002F\u002F Attach sub-components as static properties\nfunction Tabs({ defaultValue, children }) { \u002F* ... *\u002F }\n\nTabs.List  = function TabsList({ children }) { \u002F* ... *\u002F };\nTabs.Tab   = function TabsTab({ value, children }) { \u002F* ... *\u002F };\nTabs.Panel = function TabsPanel({ value, children }) { \u002F* ... *\u002F };\n\nexport default Tabs;\n\n\u002F\u002F Consumer imports one name, uses all four components\nimport Tabs from '.\u002FTabs';\n\n\u003CTabs defaultValue=\"a\">\n  \u003CTabs.List>\n    \u003CTabs.Tab value=\"a\">A\u003C\u002FTabs.Tab>\n  \u003C\u002FTabs.List>\n  \u003CTabs.Panel value=\"a\">Content\u003C\u002FTabs.Panel>\n\u003C\u002FTabs>\n```\n\nAn alternative is named exports (`export { Tabs, TabsList, TabsTab, TabsPanel }`) which\nworks well with tree-shaking but loses the visual grouping at the import site.\n\n**Rule of thumb:** Use dot-notation for libraries or shared design system components;\nnamed exports are fine for single-app usage where IDE autocomplete fills the gap.\n",{"id":353,"difficulty":121,"q":354,"a":355},"react-children-traversal","What is the React.Children traversal approach to compound components and what are its drawbacks?","Before Context, a common technique was **`React.Children.map`** with `React.cloneElement`\nto inject props directly into child elements. The parent inspects its children and injects\nthe shared state as additional props.\n\n```jsx\nfunction Tabs({ defaultValue, children }) {\n  const [active, setActive] = useState(defaultValue);\n\n  return (\n    \u003Cdiv>\n      {React.Children.map(children, child => {\n        if (!React.isValidElement(child)) return child;\n        \u002F\u002F Inject active\u002FsetActive into every direct child\n        return React.cloneElement(child, { active, setActive });\n      })}\n    \u003C\u002Fdiv>\n  );\n}\n```\n\nKey drawbacks:\n- **Breaks with nesting** — `cloneElement` only reaches direct children; wrapping a\n  `\u003CTabs.Tab>` in a `\u003Cdiv>` for layout breaks the injection chain.\n- **Type safety is lost** — injected props are not reflected in the child component's\n  declared prop types.\n- **Performance** — clones every child on every render even if state did not change.\n- **Fragile** — relies on element identity checks that break with HOCs or memoized\n  wrappers.\n\n**Rule of thumb:** Prefer Context over `cloneElement` for new compound components;\n`cloneElement` is a legacy pattern kept alive only to understand older codebases.\n",{"id":357,"difficulty":121,"q":358,"a":359},"controlled-vs-uncontrolled-compound","How do you make a compound component support both controlled and uncontrolled modes?","Follow the same **controlled\u002Funcontrolled duality** used by HTML inputs: accept an\noptional `value` + `onChange` for controlled mode, and a `defaultValue` for uncontrolled.\nInside the component, maintain internal state only when the caller passes no `value`.\n\n```jsx\nfunction Tabs({ value, defaultValue, onChange, children }) {\n  \u002F\u002F Internal state only used in uncontrolled mode\n  const [internalActive, setInternalActive] = useState(defaultValue ?? null);\n\n  \u002F\u002F Controlled if `value` prop is provided\n  const isControlled = value !== undefined;\n  const active = isControlled ? value : internalActive;\n\n  function handleChange(next) {\n    if (!isControlled) setInternalActive(next); \u002F\u002F update internal state\n    onChange?.(next);                            \u002F\u002F always notify caller\n  }\n\n  return (\n    \u003CTabsContext.Provider value={{ active, handleChange }}>\n      {children}\n    \u003C\u002FTabsContext.Provider>\n  );\n}\n```\n\n**Rule of thumb:** A component is controlled when the caller owns the value prop; never\nswitch between modes at runtime — log a warning if `value` goes from defined to undefined.\n",{"id":361,"difficulty":121,"q":362,"a":363},"implicit-vs-explicit-state","What is the difference between implicit and explicit state sharing in compound components?","**Implicit sharing** (via Context) hides the wiring from the consumer. Sub-components\nreach into Context themselves — the consumer never passes state between siblings.\n\n**Explicit sharing** means the consumer manually threads state as props, typically using\n`render props` or a `children` function pattern:\n\n```jsx\n\u002F\u002F Explicit — caller owns and passes state\n\u003CTabs>\n  {({ active, setActive }) => (\n    \u003C>\n      \u003CTabList active={active} onSelect={setActive} \u002F>\n      \u003CTabPanel active={active} \u002F>\n    \u003C\u002F>\n  )}\n\u003C\u002FTabs>\n\n\u002F\u002F Implicit — Context handles the wiring\n\u003CTabs defaultValue=\"a\">\n  \u003CTabs.List>\n    \u003CTabs.Tab value=\"a\">A\u003C\u002FTabs.Tab>  {\u002F* reads Context internally *\u002F}\n  \u003C\u002FTabs.List>\n  \u003CTabs.Panel value=\"a\">Content\u003C\u002FTabs.Panel>\n\u003C\u002FTabs>\n```\n\nImplicit sharing produces cleaner JSX and is the hallmark of the compound components\npattern. Explicit sharing via render props is more flexible (state is available anywhere\nin the subtree) but transfers more complexity to the consumer.\n\n**Rule of thumb:** Use implicit Context sharing for encapsulated design-system components;\nuse render props when the consumer genuinely needs access to internal state for custom logic.\n",{"id":365,"difficulty":96,"q":366,"a":367},"custom-hook-for-context","Why and how do you wrap a compound component's Context in a custom hook?","Wrapping the Context in a **custom hook** adds a guard that throws a descriptive error\nwhen a sub-component is used outside its parent. This is far easier to debug than the\nsilent `null` that `useContext` returns for a missing provider.\n\n```jsx\nconst TabsContext = createContext(null);\n\n\u002F\u002F Custom hook with guard\nfunction useTabs() {\n  const ctx = useContext(TabsContext);\n  if (!ctx) {\n    throw new Error(\n      'useTabs must be used within a \u003CTabs> component'\n    );\n  }\n  return ctx;\n}\n\n\u002F\u002F Sub-components use the guarded hook, not useContext directly\nfunction TabsTab({ value, children }) {\n  const { active, handleChange } = useTabs(); \u002F\u002F throws if misused\n  return (\n    \u003Cbutton onClick={() => handleChange(value)}>\n      {children}\n    \u003C\u002Fbutton>\n  );\n}\n```\n\n**Rule of thumb:** Always export the custom hook rather than the Context object; this\nprevents consumers from bypassing the guard and simplifies future refactors.\n",{"id":369,"difficulty":121,"q":370,"a":371},"typescript-typing","How do you type a compound component API with TypeScript?","You need to type three things: the **Context shape**, the **sub-component props**, and\nthe **parent component augmented with its sub-component statics**.\n\n```tsx\n\u002F\u002F 1. Type the shared context\ninterface TabsContextValue {\n  active: string;\n  handleChange: (value: string) => void;\n}\nconst TabsContext = createContext\u003CTabsContextValue | null>(null);\n\n\u002F\u002F 2. Type each sub-component normally\ninterface TabsTabProps {\n  value: string;\n  children: React.ReactNode;\n}\nfunction TabsTab({ value, children }: TabsTabProps) { \u002F* ... *\u002F }\n\n\u002F\u002F 3. Augment the parent type with static properties\ninterface TabsComponent extends React.FC\u003CTabsProps> {\n  List:  React.FC\u003C{ children: React.ReactNode }>;\n  Tab:   React.FC\u003CTabsTabProps>;\n  Panel: React.FC\u003C{ value: string; children: React.ReactNode }>;\n}\n\nconst Tabs = function Tabs({ defaultValue, children }: TabsProps) {\n  \u002F* ... *\u002F\n} as TabsComponent;\n\nTabs.List  = TabsList;\nTabs.Tab   = TabsTab;\nTabs.Panel = TabsPanel;\n```\n\n**Rule of thumb:** Use a named interface that extends `React.FC\u003CProps>` to attach\nsub-component types; this gives consumers full autocomplete on `\u003CTabs.` in the editor.\n",{"id":373,"difficulty":96,"q":374,"a":375},"real-world-accordion","Describe the compound component structure for an Accordion.","An **Accordion** is a natural fit: the root tracks which item(s) are expanded; each\n`Accordion.Item` provides a nested sub-Context for its own `value`; `Accordion.Trigger`\ntoggles expansion; `Accordion.Content` conditionally renders.\n\n```jsx\n\u002F\u002F Root: tracks open items\nfunction Accordion({ type = 'single', children }) {\n  const [open, setOpen] = useState(new Set());\n  function toggle(value) {\n    setOpen(prev => {\n      const next = new Set(type === 'single' ? [] : prev);\n      prev.has(value) ? next.delete(value) : next.add(value);\n      return next;\n    });\n  }\n  return (\n    \u003CAccordionContext.Provider value={{ open, toggle }}>\n      {children}\n    \u003C\u002FAccordionContext.Provider>\n  );\n}\n\n\u002F\u002F Item: provides its own value to children via a second context\nAccordion.Item = function AccordionItem({ value, children }) {\n  return (\n    \u003CAccordionItemContext.Provider value={value}>\n      {children}\n    \u003C\u002FAccordionItemContext.Provider>\n  );\n};\n\n\u002F\u002F Trigger reads item context to know which value to toggle\nAccordion.Trigger = function AccordionTrigger({ children }) {\n  const value  = useContext(AccordionItemContext);\n  const { open, toggle } = useAccordion();\n  return (\n    \u003Cbutton aria-expanded={open.has(value)} onClick={() => toggle(value)}>\n      {children}\n    \u003C\u002Fbutton>\n  );\n};\n```\n\n**Rule of thumb:** Nested Context layers are fine — use one for the collection-level\nstate and one for each item's identity; keep both Context objects private.\n",{"id":377,"difficulty":121,"q":378,"a":379},"flexible-composition","How does the compound component pattern enable flexible composition that monolithic components cannot?","With a monolithic component the **layout is fixed** — the component renders its own\nmarkup structure. With compound components the **consumer controls the layout** by placing\nsub-components anywhere in JSX, interspersing other elements freely.\n\n```jsx\n\u002F\u002F Monolithic: layout is baked in, no way to add a badge next to a tab label\n\u003CTabs tabs={[{ label: 'A' }, { label: 'B' }]} \u002F>\n\n\u002F\u002F Compound: consumer inserts arbitrary content between or inside sub-components\n\u003CTabs defaultValue=\"a\">\n  \u003Cheader className=\"flex justify-between\">\n    \u003CTabs.List>\n      \u003CTabs.Tab value=\"a\">\n        Dashboard \u003CBadge count={3} \u002F> {\u002F* freely inserted *\u002F}\n      \u003C\u002FTabs.Tab>\n      \u003CTabs.Tab value=\"b\">Settings\u003C\u002FTabs.Tab>\n    \u003C\u002FTabs.List>\n    \u003CUserMenu \u002F>  {\u002F* unrelated element, same row *\u002F}\n  \u003C\u002Fheader>\n\n  \u003CTabs.Panel value=\"a\">\u003CDashboardContent \u002F>\u003C\u002FTabs.Panel>\n  \u003CTabs.Panel value=\"b\">\u003CSettingsContent \u002F>\u003C\u002FTabs.Panel>\n\u003C\u002FTabs>\n```\n\n**Rule of thumb:** If different callers need different layouts or want to inject elements\nbetween parts of a component, compound components are the right abstraction; if every\ncaller wants the same layout, a simpler prop-driven component is sufficient.\n",{"id":381,"difficulty":96,"q":382,"a":383},"when-not-to-use","When should you NOT use the compound component pattern?","Compound components add indirection and a module-level Context. They are the wrong choice\nwhen:\n\n- **The component is simple** — a `\u003CButton>` with a loading state does not warrant a\n  compound API; a single `loading` boolean prop is clearer.\n- **Sub-components are always rendered together** — if callers never need to rearrange the\n  parts, the flexibility is overhead with no benefit.\n- **Server components** — React Server Components cannot use Context, so the pattern\n  requires pushing the compound root to a client boundary.\n- **Over-engineering** — a team unfamiliar with the pattern will find it harder to\n  maintain than a straightforward props API.\n\n```jsx\n\u002F\u002F Overkill — compound component for something trivially prop-driven\n\u003CButton.Root>\n  \u003CButton.Icon name=\"save\" \u002F>\n  \u003CButton.Label>Save\u003C\u002FButton.Label>\n\u003C\u002FButton.Root>\n\n\u002F\u002F Just use props\n\u003CButton icon=\"save\" loading={saving}>Save\u003C\u002FButton>\n```\n\n**Rule of thumb:** Use compound components when callers regularly need layout control\nover multiple coordinated sub-pieces; prefer simple props when one \"shape\" serves everyone.\n",{"id":385,"difficulty":121,"q":386,"a":387},"select-compound-example","Walk through a minimal compound component implementation of a custom Select.","A custom **Select** needs: the root to track open\u002Fvalue state; a `Trigger` to open the\ndropdown; a `List` to contain options; and `Option` items that close the list on selection.\n\n```jsx\nconst SelectCtx = createContext(null);\nfunction useSelect() {\n  const ctx = useContext(SelectCtx);\n  if (!ctx) throw new Error('Must be inside \u003CSelect>');\n  return ctx;\n}\n\nfunction Select({ value, onChange, children }) {\n  const [open, setOpen] = useState(false);\n  return (\n    \u003CSelectCtx.Provider value={{ value, onChange, open, setOpen }}>\n      \u003Cdiv className=\"relative\">{children}\u003C\u002Fdiv>\n    \u003C\u002FSelectCtx.Provider>\n  );\n}\n\nSelect.Trigger = function SelectTrigger({ children }) {\n  const { value, open, setOpen } = useSelect();\n  return (\n    \u003Cbutton onClick={() => setOpen(o => !o)} aria-haspopup=\"listbox\">\n      {value ?? children}  {\u002F* show selected value or placeholder *\u002F}\n    \u003C\u002Fbutton>\n  );\n};\n\nSelect.Option = function SelectOption({ value, children }) {\n  const { onChange, setOpen } = useSelect();\n  return (\n    \u003Cli\n      role=\"option\"\n      onClick={() => { onChange(value); setOpen(false); }}\n    >\n      {children}\n    \u003C\u002Fli>\n  );\n};\n```\n\n**Rule of thumb:** For accessible custom selects, layer the compound pattern on top of\n`aria-*` attributes rather than replacing native `\u003Cselect>` unless custom styling\ngenuinely requires it.\n",{"id":389,"difficulty":121,"q":390,"a":391},"menu-compound-example","How would you implement a compound Menu component with keyboard navigation?","A **Menu** follows the same Context pattern but adds `useRef` and keyboard event handling\nto support arrow-key navigation and focus management.\n\n```jsx\nconst MenuCtx = createContext(null);\n\nfunction Menu({ children }) {\n  const [open, setOpen]       = useState(false);\n  const [focused, setFocused] = useState(0);\n  const itemRefs              = useRef([]);\n\n  function handleKeyDown(e) {\n    if (e.key === 'ArrowDown') {\n      const next = (focused + 1) % itemRefs.current.length;\n      setFocused(next);\n      itemRefs.current[next]?.focus(); \u002F\u002F move DOM focus\n    }\n    if (e.key === 'Escape') setOpen(false);\n  }\n\n  return (\n    \u003CMenuCtx.Provider value={{ open, setOpen, focused, setFocused, itemRefs }}>\n      \u003Cdiv onKeyDown={handleKeyDown}>{children}\u003C\u002Fdiv>\n    \u003C\u002FMenuCtx.Provider>\n  );\n}\n\nMenu.Item = function MenuItem({ index, onSelect, children }) {\n  const { itemRefs } = useContext(MenuCtx);\n  return (\n    \u003Cbutton\n      ref={el => (itemRefs.current[index] = el)} \u002F\u002F register ref\n      role=\"menuitem\"\n      tabIndex={-1}\n      onClick={onSelect}\n    >\n      {children}\n    \u003C\u002Fbutton>\n  );\n};\n```\n\n**Rule of thumb:** Store an array of item refs in the root Context so the root's\n`keyDown` handler can programmatically focus items without each item knowing about\nits siblings.\n",{"id":393,"difficulty":208,"q":394,"a":395},"default-context-value","What should you pass as the default value to createContext in a compound component and why?","The **default Context value** is used only when a component renders outside any matching\n`Provider` — which for compound components means the sub-component is being used\nincorrectly. There are two schools of thought:\n\n1. **`null` + runtime guard** (recommended): pass `null` as the default and throw in the\n   custom hook. This surfaces misuse as a clear error rather than a silent `undefined`.\n\n2. **Typed fallback**: provide a full default shape. This prevents errors but silently\n   allows misuse, making bugs hard to trace.\n\n```tsx\n\u002F\u002F Option 1: null default + guard (preferred)\nconst TabsCtx = createContext\u003CTabsContextValue | null>(null);\n\nfunction useTabs(): TabsContextValue {\n  const ctx = useContext(TabsCtx);\n  if (ctx === null) {\n    throw new Error('\u003CTabs.Tab> must be rendered inside \u003CTabs>');\n  }\n  return ctx;\n}\n\n\u002F\u002F Option 2: full default (use only for optional composition)\nconst TabsCtx = createContext\u003CTabsContextValue>({\n  active: '',\n  handleChange: () => {},  \u002F\u002F no-op silently\n});\n```\n\n**Rule of thumb:** Use `null` with a guard for required compound parents; use a no-op\ndefault only when a sub-component is genuinely optional (e.g., a tooltip that renders\nstandalone without a parent wrapper).\n",{"id":397,"difficulty":121,"q":398,"a":399},"testing-compound-components","How do you test compound components with React Testing Library?","Test compound components **through their full composed API** — render the parent with\nsub-components just as a consumer would. Avoid importing and rendering sub-components\nin isolation because they depend on the Context being present.\n\n```jsx\nimport { render, screen } from '@testing-library\u002Freact';\nimport userEvent from '@testing-library\u002Fuser-event';\nimport Tabs from '.\u002FTabs';\n\ntest('switches active panel on tab click', async () => {\n  render(\n    \u003CTabs defaultValue=\"a\">\n      \u003CTabs.List>\n        \u003CTabs.Tab value=\"a\">Tab A\u003C\u002FTabs.Tab>\n        \u003CTabs.Tab value=\"b\">Tab B\u003C\u002FTabs.Tab>\n      \u003C\u002FTabs.List>\n      \u003CTabs.Panel value=\"a\">Content A\u003C\u002FTabs.Panel>\n      \u003CTabs.Panel value=\"b\">Content B\u003C\u002FTabs.Panel>\n    \u003C\u002FTabs>\n  );\n\n  \u002F\u002F Initial state\n  expect(screen.getByText('Content A')).toBeVisible();\n  expect(screen.queryByText('Content B')).not.toBeVisible();\n\n  \u002F\u002F Interact and assert\n  await userEvent.click(screen.getByRole('button', { name: 'Tab B' }));\n  expect(screen.getByText('Content B')).toBeVisible();\n});\n```\n\nFor controlled compound components, test that `onChange` is called with the correct\nvalue and that the displayed state matches the controlled `value` prop.\n\n**Rule of thumb:** Test behavior (what the user sees and can do), not implementation\n(which Context value changed); this keeps tests resilient to internal refactors.\n",{"id":401,"difficulty":208,"q":402,"a":403},"performance-concerns","What performance concerns exist with Context-based compound components and how do you address them?","Every consumer of a Context re-renders whenever the **entire Context value changes**.\nIf the parent passes a new object reference on every render (even if the values are the\nsame), all sub-components re-render unnecessarily.\n\n```jsx\n\u002F\u002F Problem: new object reference on every render\nfunction Tabs({ children }) {\n  const [active, setActive] = useState('a');\n  return (\n    \u003CTabsCtx.Provider value={{ active, setActive }}> {\u002F* new ref each time *\u002F}\n      {children}\n    \u003C\u002FTabsCtx.Provider>\n  );\n}\n\n\u002F\u002F Fix 1: useMemo to stabilize the context object\nfunction Tabs({ children }) {\n  const [active, setActive] = useState('a');\n  const ctx = useMemo(() => ({ active, setActive }), [active]);\n  return \u003CTabsCtx.Provider value={ctx}>{children}\u003C\u002FTabsCtx.Provider>;\n}\n\n\u002F\u002F Fix 2: split into separate contexts (state vs dispatch)\nconst TabsStateCtx    = createContext(null); \u002F\u002F triggers re-render on change\nconst TabsDispatchCtx = createContext(null); \u002F\u002F stable — setActive never changes\n```\n\nSplitting into a state context and a dispatch context is the most effective approach:\ncomponents that only call `setActive` (like `Tabs.Tab`) subscribe to `TabsDispatchCtx`\nand never re-render when the active value changes.\n\n**Rule of thumb:** Start with a single merged Context; split into state + dispatch only\nwhen profiling confirms unnecessary re-renders are a real bottleneck.\n",{"id":405,"difficulty":208,"q":406,"a":407},"server-components-limitation","How does the React Server Components model affect compound components that use Context?","**React Server Components (RSC)** cannot use Context — `createContext` and `useContext`\nare client-only APIs. This means compound components that rely on Context must be\n**client components** (marked `'use client'`).\n\nThe practical implication is a **client boundary** at the compound component root:\n\n```tsx\n\u002F\u002F tabs.tsx — must be a client component\n'use client';\nimport { createContext, useContext, useState } from 'react';\n\u002F\u002F ... full compound component implementation\n\n\u002F\u002F page.tsx — server component; Tabs forces a client subtree\nimport Tabs from '.\u002Ftabs';\n\nexport default function Page() {\n  return (\n    \u002F\u002F Everything inside \u003CTabs> runs on the client\n    \u003CTabs defaultValue=\"a\">\n      \u003CTabs.Tab value=\"a\">A\u003C\u002FTabs.Tab>\n      \u003CTabs.Panel value=\"a\">\n        {\u002F* Server components can be passed as children to client components *\u002F}\n        \u003CServerDataTable \u002F>\n      \u003C\u002FTabs.Panel>\n    \u003C\u002FTabs>\n  );\n}\n```\n\nYou can still pass **server-rendered content as children** into a client compound\ncomponent — RSC allows server components to be children of client components. The\nserver component renders its output and passes it as serialized props.\n\n**Rule of thumb:** Mark the compound root (and sub-components that use Context) as\n`'use client'`; keep the data-fetching server components as their consumers' children\nrather than embedding them inside the compound component module.\n",{"id":409,"difficulty":121,"q":410,"a":411},"vs-render-props","How do compound components compare to render props as a composition pattern?","Both patterns share state with consumers, but they differ in where the consumer\ninteracts with that state.\n\n**Render props** — the consumer receives state as function arguments and can use it\nanywhere within the callback, including conditional logic. More explicit and flexible,\nbut produces deeply indented JSX (\"callback hell\").\n\n**Compound components** — state is accessed implicitly inside sub-components via Context.\nProduces flat, declarative JSX that reads like HTML.\n\n```jsx\n\u002F\u002F Render props: state is explicit, layout is free-form\n\u003CTabs>\n  {({ active, setActive }) => (\n    \u003Cdiv className=\"custom-layout\">\n      \u003Cbutton onClick={() => setActive('a')}\n        style={{ fontWeight: active === 'a' ? 'bold' : 'normal' }}>A\u003C\u002Fbutton>\n      {active === 'a' && \u003Cdiv>Panel A\u003C\u002Fdiv>}\n    \u003C\u002Fdiv>\n  )}\n\u003C\u002FTabs>\n\n\u002F\u002F Compound: declarative, less flexible but cleaner\n\u003CTabs defaultValue=\"a\">\n  \u003CTabs.Tab value=\"a\">A\u003C\u002FTabs.Tab>\n  \u003CTabs.Panel value=\"a\">Panel A\u003C\u002FTabs.Panel>\n\u003C\u002FTabs>\n```\n\n**Rule of thumb:** Prefer compound components for design-system UI widgets where layout\nflexibility is bounded; prefer render props when consumers need the raw state to drive\narbitrary custom logic.\n",{"id":413,"difficulty":208,"q":414,"a":415},"accessibility-in-compound","How do you handle ARIA attributes in compound components to ensure accessibility?","Compound components should **generate and wire ARIA attributes automatically** so\nconsumers don't need to add them manually. The Context provides stable IDs and\nexpanded\u002Fselected state that sub-components apply to their DOM elements.\n\n```jsx\nfunction Tabs({ defaultValue, id: baseId = useId(), children }) {\n  const [active, setActive] = useState(defaultValue);\n  return (\n    \u003CTabsCtx.Provider value={{ active, setActive, baseId }}>\n      {children}\n    \u003C\u002FTabsCtx.Provider>\n  );\n}\n\nTabs.Tab = function TabsTab({ value, children }) {\n  const { active, setActive, baseId } = useTabs();\n  return (\n    \u003Cbutton\n      id={`${baseId}-tab-${value}`}       \u002F\u002F stable ID\n      role=\"tab\"\n      aria-selected={active === value}\n      aria-controls={`${baseId}-panel-${value}`} \u002F\u002F links to panel\n      onClick={() => setActive(value)}\n    >\n      {children}\n    \u003C\u002Fbutton>\n  );\n};\n\nTabs.Panel = function TabsPanel({ value, children }) {\n  const { active, baseId } = useTabs();\n  return (\n    \u003Cdiv\n      id={`${baseId}-panel-${value}`}     \u002F\u002F matches aria-controls\n      role=\"tabpanel\"\n      aria-labelledby={`${baseId}-tab-${value}`}\n      hidden={active !== value}\n    >\n      {children}\n    \u003C\u002Fdiv>\n  );\n};\n```\n\n**Rule of thumb:** Generate IDs with `useId()` inside the root component and share them\nvia Context so `aria-controls`\u002F`aria-labelledby` pairs are always consistent without\nrequiring consumers to manage IDs manually.\n",19,{"description":94},"React compound components interview questions — implicit state sharing, Context-based APIs, flexible composition, and component slot patterns.","react\u002Fpatterns\u002Fcompound-components","2026-06-24","TLvrPCxxzntq3ZbMDXCJk4oBf_smSWVPIoVgZQ_K2G0",{"id":423,"title":424,"body":425,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":429,"navigation":100,"order":13,"path":431,"questions":432,"questionsCount":493,"related":169,"seo":494,"seoDescription":495,"stem":496,"subtopic":497,"topic":45,"topicSlug":47,"updated":420,"__hash__":498},"qa\u002Freact\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation.md","Virtual Dom Reconciliation",{"type":91,"value":426,"toc":427},[],{"title":94,"searchDepth":11,"depth":11,"links":428},[],{"subtopicSlug":430},"virtual-dom-reconciliation","\u002Freact\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation",[433,437,441,445,449,453,457,461,465,469,473,477,481,485,489],{"id":434,"difficulty":96,"q":435,"a":436},"what-is-virtual-dom","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":438,"difficulty":96,"q":439,"a":440},"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":442,"difficulty":121,"q":443,"a":444},"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":446,"difficulty":121,"q":447,"a":448},"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":450,"difficulty":96,"q":451,"a":452},"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":454,"difficulty":121,"q":455,"a":456},"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":458,"difficulty":121,"q":459,"a":460},"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":462,"difficulty":208,"q":463,"a":464},"bailout-conditions","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":466,"difficulty":121,"q":467,"a":468},"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":470,"difficulty":121,"q":471,"a":472},"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":474,"difficulty":208,"q":475,"a":476},"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":478,"difficulty":208,"q":479,"a":480},"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":482,"difficulty":96,"q":483,"a":484},"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":486,"difficulty":121,"q":487,"a":488},"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":490,"difficulty":121,"q":491,"a":492},"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,{"description":94},"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","Eg1x64GHznlb0XcJS2vShptyDF0E5StXiLMG8z1yzVw",{"id":500,"title":501,"body":502,"description":94,"difficulty":96,"extension":97,"framework":10,"frameworkSlug":8,"meta":506,"navigation":100,"order":13,"path":507,"questions":508,"questionsCount":589,"related":169,"seo":590,"seoDescription":591,"stem":592,"subtopic":501,"topic":54,"topicSlug":56,"updated":420,"__hash__":593},"qa\u002Freact\u002Frouting\u002Frouting-basics.md","Routing Basics",{"type":91,"value":503,"toc":504},[],{"title":94,"searchDepth":11,"depth":11,"links":505},[],{},"\u002Freact\u002Frouting\u002Frouting-basics",[509,513,517,521,525,529,533,537,541,545,549,553,557,561,565,569,573,577,581,585],{"id":510,"difficulty":96,"q":511,"a":512},"what-is-client-side-routing","What is client-side routing and why does React use it?","**Client-side routing** means the browser never performs a full page reload when navigating between views. Instead, JavaScript intercepts navigation events, updates the URL via the **History API**, and re-renders the appropriate component tree — giving the feel of a multi-page app without the network round-trip.\n\n```jsx\n\u002F\u002F Without client-side routing: browser fetches \u002Fabout from the server\n\u002F\u002F With client-side routing: JS swaps components and pushes to history\nimport { Link } from 'react-router-dom';\n\nfunction Nav() {\n  return (\n    \u002F\u002F Clicking this never triggers a server request\n    \u003CLink to=\"\u002Fabout\">About\u003C\u002FLink>\n  );\n}\n```\n\n**Rule of thumb:** Use client-side routing in any React SPA so users get instant navigation and the app shell (header, sidebar) stays mounted across route changes.\n",{"id":514,"difficulty":96,"q":515,"a":516},"install-react-router-v6","How do you install and minimally set up React Router v6?","Install the package, then wrap your app in **BrowserRouter** (or use **createBrowserRouter** for the data router API). Every router hook and component must live inside that provider.\n\n```jsx\n\u002F\u002F 1. npm install react-router-dom\n\n\u002F\u002F 2. main.jsx — wrap the root\nimport { BrowserRouter } from 'react-router-dom';\nimport { createRoot } from 'react-dom\u002Fclient';\nimport App from '.\u002FApp';\n\ncreateRoot(document.getElementById('root')).render(\n  \u003CBrowserRouter>   {\u002F* provides routing context to the whole tree *\u002F}\n    \u003CApp \u002F>\n  \u003C\u002FBrowserRouter>\n);\n```\n\n**Rule of thumb:** Always place `BrowserRouter` (or its equivalent) at the very root of your component tree, not inside a page component — otherwise hooks like `useNavigate` will throw a context error.\n",{"id":518,"difficulty":121,"q":519,"a":520},"create-browser-router-api","What is createBrowserRouter and how does it differ from BrowserRouter?","**createBrowserRouter** is the v6.4+ **data router** API. It co-locates routes with **loaders** and **actions** (server-style data fetching\u002Fmutation), and it is the recommended approach for new apps. **BrowserRouter** is the legacy component wrapper that does not support loaders\u002Factions.\n\n```jsx\nimport { createBrowserRouter, RouterProvider } from 'react-router-dom';\nimport Root from '.\u002FRoot';\nimport Home from '.\u002FHome';\nimport About from '.\u002FAbout';\n\nconst router = createBrowserRouter([\n  {\n    path: '\u002F',\n    element: \u003CRoot \u002F>,   \u002F\u002F layout with \u003COutlet \u002F>\n    children: [\n      { index: true, element: \u003CHome \u002F> },\n      { path: 'about', element: \u003CAbout \u002F> },\n    ],\n  },\n]);\n\n\u002F\u002F main.jsx\n\u003CRouterProvider router={router} \u002F>\n```\n\n**Rule of thumb:** Reach for `createBrowserRouter` in greenfield projects; use `BrowserRouter` only when you need to stay on the simpler JSX-only API or are migrating incrementally.\n",{"id":522,"difficulty":96,"q":523,"a":524},"route-and-routes-components","What are the Routes and Route components, and how do they work together?","**Routes** is a container that picks the single best-matching **Route** from its children. **Route** declares a `path` and the `element` to render when the URL matches. In v6, `Routes` replaces the v5 `Switch` and always does **exclusive** (first-match) selection.\n\n```jsx\nimport { Routes, Route } from 'react-router-dom';\nimport Home from '.\u002FHome';\nimport About from '.\u002FAbout';\nimport NotFound from '.\u002FNotFound';\n\nfunction App() {\n  return (\n    \u003CRoutes>\n      \u003CRoute path=\"\u002F\" element={\u003CHome \u002F>} \u002F>\n      \u003CRoute path=\"\u002Fabout\" element={\u003CAbout \u002F>} \u002F>\n      \u003CRoute path=\"*\" element={\u003CNotFound \u002F>} \u002F>  {\u002F* catch-all *\u002F}\n    \u003C\u002FRoutes>\n  );\n}\n```\n\n**Rule of thumb:** Always keep your top-level `Routes` block in one place (usually `App.jsx`) so route precedence is obvious at a glance.\n",{"id":526,"difficulty":96,"q":527,"a":528},"exact-matching-v6","How does path matching work in React Router v6 — is there an \"exact\" prop?","In v6 all routes match **exactly** by default — the `exact` prop was removed. A route with `path=\"\u002F\"` will **not** match `\u002Fabout`; you no longer need to add `exact` everywhere. The router also ranks routes by specificity, so more-specific paths win over less-specific ones.\n\n```jsx\n\u003CRoutes>\n  {\u002F* v5 needed exact on \"\u002F\", v6 does not *\u002F}\n  \u003CRoute path=\"\u002F\"        element={\u003CHome \u002F>} \u002F>   {\u002F* only \"\u002F\" *\u002F}\n  \u003CRoute path=\"\u002Fusers\"   element={\u003CUsers \u002F>} \u002F>  {\u002F* only \"\u002Fusers\" *\u002F}\n  \u003CRoute path=\"\u002Fusers\u002F:id\" element={\u003CUser \u002F>} \u002F> {\u002F* wins over \"\u002Fusers\" for \"\u002Fusers\u002F42\" *\u002F}\n\u003C\u002FRoutes>\n```\n\n**Rule of thumb:** Remove all `exact` props when migrating from v5 to v6 — they are a no-op and will generate a warning.\n",{"id":530,"difficulty":121,"q":531,"a":532},"nested-routes","How do nested routes work in React Router v6?","Nest **Route** elements inside a parent **Route** to model layout nesting. The parent renders its `element` and places an **Outlet** component where child routes should appear. The child path is **relative** to the parent — no leading slash needed.\n\n```jsx\n\u003CRoutes>\n  \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboardLayout \u002F>}>\n    {\u002F* relative paths — resolved as \u002Fdashboard\u002Foverview *\u002F}\n    \u003CRoute path=\"overview\"  element={\u003COverview \u002F>} \u002F>\n    \u003CRoute path=\"settings\"  element={\u003CSettings \u002F>} \u002F>\n  \u003C\u002FRoute>\n\u003C\u002FRoutes>\n\n\u002F\u002F DashboardLayout.jsx\nimport { Outlet } from 'react-router-dom';\n\nfunction DashboardLayout() {\n  return (\n    \u003Cdiv>\n      \u003CSidebar \u002F>\n      \u003Cmain>\n        \u003COutlet \u002F>  {\u002F* child route renders here *\u002F}\n      \u003C\u002Fmain>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Use nested routes whenever multiple pages share a persistent layout (nav, sidebar, header) — the layout component stays mounted while only the `Outlet` content swaps.\n",{"id":534,"difficulty":121,"q":535,"a":536},"outlet-component","What is the Outlet component and when is it required?","**Outlet** is a placeholder rendered by a parent route's `element`; it marks the spot where the matched child route's component should appear. Without `Outlet`, child routes match in the URL but their elements are never rendered to the screen.\n\n```jsx\nimport { Outlet, NavLink } from 'react-router-dom';\n\nfunction AdminLayout() {\n  return (\n    \u003Cdiv className=\"admin\">\n      \u003Cnav>\n        \u003CNavLink to=\"users\">Users\u003C\u002FNavLink>\n        \u003CNavLink to=\"reports\">Reports\u003C\u002FNavLink>\n      \u003C\u002Fnav>\n      {\u002F* matched child route appears here *\u002F}\n      \u003COutlet \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Every route that has children in the route tree must render `\u003COutlet \u002F>` somewhere in its element, or those children will silently not appear.\n",{"id":538,"difficulty":121,"q":539,"a":540},"index-routes","What is an index route and when should you use one?","An **index route** is a child route with `index={true}` instead of a `path`. It renders inside the parent's `Outlet` when the URL exactly matches the parent path — serving as the \"default child\" view.\n\n```jsx\n\u003CRoutes>\n  \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboardLayout \u002F>}>\n    {\u002F* renders at \u002Fdashboard when no child segment follows *\u002F}\n    \u003CRoute index element={\u003CDashboardHome \u002F>} \u002F>\n    \u003CRoute path=\"users\"   element={\u003CUsers \u002F>} \u002F>\n    \u003CRoute path=\"reports\" element={\u003CReports \u002F>} \u002F>\n  \u003C\u002FRoute>\n\u003C\u002FRoutes>\n```\n\n**Rule of thumb:** Add an index route to every layout route so the `Outlet` is never empty — without it, visiting the parent path exactly renders a blank content area.\n",{"id":542,"difficulty":96,"q":543,"a":544},"link-component","What is the Link component and why should you use it instead of an anchor tag?","**Link** renders an `\u003Ca>` element but intercepts the click, calls `history.pushState`, and re-renders the app without a page reload. A plain `\u003Ca href=\"...\">` would trigger a full browser navigation, destroying React state.\n\n```jsx\nimport { Link } from 'react-router-dom';\n\nfunction Nav() {\n  return (\n    \u003Cnav>\n      \u003CLink to=\"\u002F\">Home\u003C\u002FLink>           {\u002F* absolute path *\u002F}\n      \u003CLink to=\"settings\">Settings\u003C\u002FLink> {\u002F* relative to current route *\u002F}\n      \u003CLink to=\"\u002Fusers\u002F42\">User 42\u003C\u002FLink>\n    \u003C\u002Fnav>\n  );\n}\n```\n\n**Rule of thumb:** Always use `Link` (or `NavLink`) for in-app navigation; reserve plain `\u003Ca>` tags only for external URLs or files that require a real browser navigation.\n",{"id":546,"difficulty":121,"q":547,"a":548},"navlink-component","How does NavLink differ from Link, and how do you style the active state in v6?","**NavLink** is a `Link` that knows whether its destination is the current route. In v6 it automatically applies an `\"active\"` CSS class (and optionally `\"pending\"`) when matched. The v5 `activeClassName` \u002F `activeStyle` props were removed; instead, pass a **callback** to `className` or `style` that receives `{ isActive }`.\n\n```jsx\nimport { NavLink } from 'react-router-dom';\n\nfunction Nav() {\n  return (\n    \u003Cnav>\n      \u003CNavLink\n        to=\"\u002Fdashboard\"\n        className={({ isActive }) =>\n          isActive ? 'nav-link active' : 'nav-link'\n        }\n      >\n        Dashboard\n      \u003C\u002FNavLink>\n\n      {\u002F* or inline style callback *\u002F}\n      \u003CNavLink\n        to=\"\u002Fsettings\"\n        style={({ isActive }) => ({ fontWeight: isActive ? 700 : 400 })}\n      >\n        Settings\n      \u003C\u002FNavLink>\n    \u003C\u002Fnav>\n  );\n}\n```\n\n**Rule of thumb:** Use `NavLink` for navigation menus where visual feedback on the active item matters; use plain `Link` everywhere else.\n",{"id":550,"difficulty":121,"q":551,"a":552},"use-navigate-hook","How do you programmatically navigate in React Router v6?","The **useNavigate** hook returns a `navigate` function. Call it with a path string to push a new entry, or pass `{ replace: true }` to replace the current history entry (useful after form submissions). Negative numbers (`navigate(-1)`) go back in history.\n\n```jsx\nimport { useNavigate } from 'react-router-dom';\n\nfunction LoginForm() {\n  const navigate = useNavigate();\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    await login(formData);\n    \u002F\u002F push to dashboard after successful login\n    navigate('\u002Fdashboard');\n    \u002F\u002F or replace so the user can't go \"back\" to the login page:\n    \u002F\u002F navigate('\u002Fdashboard', { replace: true });\n  }\n\n  return \u003Cform onSubmit={handleSubmit}>...\u003C\u002Fform>;\n}\n```\n\n**Rule of thumb:** Use `navigate(path, { replace: true })` after any action that should not remain in the browser's history stack (login redirects, post-submit confirmations).\n",{"id":554,"difficulty":96,"q":555,"a":556},"catch-all-404-route","How do you create a 404 \u002F catch-all route in React Router v6?","Add a `\u003CRoute path=\"*\">` as the last child of your `Routes`. The wildcard `*` matches any URL not claimed by earlier routes, making it the conventional 404 handler.\n\n```jsx\nimport NotFound from '.\u002FNotFound';\n\nfunction App() {\n  return (\n    \u003CRoutes>\n      \u003CRoute path=\"\u002F\"       element={\u003CHome \u002F>} \u002F>\n      \u003CRoute path=\"\u002Fabout\"  element={\u003CAbout \u002F>} \u002F>\n      \u003CRoute path=\"\u002Fusers\u002F:id\" element={\u003CUserProfile \u002F>} \u002F>\n      {\u002F* catches everything else *\u002F}\n      \u003CRoute path=\"*\" element={\u003CNotFound \u002F>} \u002F>\n    \u003C\u002FRoutes>\n  );\n}\n```\n\n**Rule of thumb:** Always include a `path=\"*\"` route — without it, unmatched URLs render nothing, which silently looks broken to users.\n",{"id":558,"difficulty":121,"q":559,"a":560},"history-mode-vs-hash-mode","What is the difference between BrowserRouter (history mode) and HashRouter (hash mode)?","**BrowserRouter** uses the HTML5 **History API** (`pushState`) and produces clean URLs like `\u002Fabout`. It requires the server to serve `index.html` for every path. **HashRouter** encodes the route in the URL hash (`\u002F#\u002Fabout`) — the hash is never sent to the server, so it works on any static host without server config.\n\n```jsx\n\u002F\u002F History mode — clean URLs, needs server catch-all\nimport { BrowserRouter } from 'react-router-dom';\n\u003CBrowserRouter>\u003CApp \u002F>\u003C\u002FBrowserRouter>\n\n\u002F\u002F Hash mode — works on GitHub Pages \u002F plain file servers\nimport { HashRouter } from 'react-router-dom';\n\u003CHashRouter>\u003CApp \u002F>\u003C\u002FHashRouter>\n\n\u002F\u002F Server catch-all example (Express)\n\u002F\u002F app.get('*', (req, res) => res.sendFile('index.html'));\n```\n\n**Rule of thumb:** Prefer `BrowserRouter` with a server catch-all for production apps; use `HashRouter` only when you have no control over server configuration (e.g., GitHub Pages, local `file:\u002F\u002F` serving).\n",{"id":562,"difficulty":121,"q":563,"a":564},"scroll-restoration","How does scroll restoration work with React Router v6?","By default browsers try to restore scroll position, but client-side navigation breaks this because the DOM updates asynchronously. React Router v6 ships a **ScrollRestoration** component (data router only) that saves and restores scroll position per URL entry. For `BrowserRouter` apps, a common manual solution is a `ScrollToTop` component that calls `window.scrollTo(0, 0)` on route change.\n\n```jsx\n\u002F\u002F Data router approach (createBrowserRouter)\nimport { ScrollRestoration } from 'react-router-dom';\n\nfunction Root() {\n  return (\n    \u003C>\n      \u003CNav \u002F>\n      \u003COutlet \u002F>\n      \u003CScrollRestoration \u002F>  {\u002F* place once in the root layout *\u002F}\n    \u003C\u002F>\n  );\n}\n\n\u002F\u002F Manual approach for BrowserRouter\nimport { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\n\nfunction ScrollToTop() {\n  const { pathname } = useLocation();\n  useEffect(() => { window.scrollTo(0, 0); }, [pathname]);\n  return null;\n}\n```\n\n**Rule of thumb:** Always handle scroll restoration explicitly — without it, users navigating back to a long page will be stranded at the bottom.\n",{"id":566,"difficulty":121,"q":567,"a":568},"relative-paths-nested-routes","What is the gotcha with relative paths in nested routes?","Child route `path` values inside a `\u003CRoute>` parent are **relative** — do not add a leading slash. A leading slash makes the path absolute, which breaks nesting because it is resolved from the root instead of relative to the parent.\n\n```jsx\n\u002F\u002F WRONG — leading slash makes children absolute paths\n\u003CRoute path=\"\u002Fsettings\" element={\u003CSettingsLayout \u002F>}>\n  \u003CRoute path=\"\u002Fsettings\u002Fprofile\"  element={\u003CProfile \u002F>} \u002F>  {\u002F* breaks *\u002F}\n  \u003CRoute path=\"\u002Fsettings\u002Fsecurity\" element={\u003CSecurity \u002F>} \u002F> {\u002F* breaks *\u002F}\n\u003C\u002FRoute>\n\n\u002F\u002F CORRECT — relative paths, no leading slash\n\u003CRoute path=\"\u002Fsettings\" element={\u003CSettingsLayout \u002F>}>\n  \u003CRoute path=\"profile\"  element={\u003CProfile \u002F>} \u002F>  {\u002F* \u002Fsettings\u002Fprofile *\u002F}\n  \u003CRoute path=\"security\" element={\u003CSecurity \u002F>} \u002F> {\u002F* \u002Fsettings\u002Fsecurity *\u002F}\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Never prefix child route paths with `\u002F` — keep them relative so React Router can compose parent + child segments correctly.\n",{"id":570,"difficulty":96,"q":571,"a":572},"use-location-hook","What does the useLocation hook return and when would you use it?","**useLocation** returns the current **location object** with `pathname`, `search`, `hash`, `state`, and `key`. It is useful for reading query params, animating on route change, or passing state between routes without putting it in the URL.\n\n```jsx\nimport { useLocation } from 'react-router-dom';\n\nfunction Analytics() {\n  const location = useLocation();\n\n  useEffect(() => {\n    \u002F\u002F fire a page-view event on every navigation\n    trackPageView(location.pathname);\n  }, [location.pathname]);\n\n  return null;\n}\n\n\u002F\u002F Passing state via Link and reading it with useLocation\n\u003CLink to=\"\u002Fconfirm\" state={{ orderId: 42 }}>Confirm\u003C\u002FLink>\n\nfunction ConfirmPage() {\n  const { state } = useLocation();\n  return \u003Cp>Order {state?.orderId} confirmed\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Use `useLocation` whenever you need to react to URL changes without controlling the route — analytics, animations, and reading navigation state are the classic cases.\n",{"id":574,"difficulty":96,"q":575,"a":576},"use-params-hook","How do you read dynamic URL segments in React Router v6?","Declare a dynamic segment in the route path with a colon prefix (`:paramName`). Inside the matched component, read it with the **useParams** hook, which returns an object keyed by every named segment in the route.\n\n```jsx\n\u002F\u002F Route declaration\n\u003CRoute path=\"\u002Fusers\u002F:userId\u002Fposts\u002F:postId\" element={\u003CPost \u002F>} \u002F>\n\n\u002F\u002F Component\nimport { useParams } from 'react-router-dom';\n\nfunction Post() {\n  const { userId, postId } = useParams();\n  \u002F\u002F userId and postId are always strings — convert if needed\n  return \u003Cp>User {userId}, Post {postId}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Always coerce URL params to the correct type (`Number(userId)`) before using them in logic — `useParams` always returns strings regardless of what you stored.\n",{"id":578,"difficulty":121,"q":579,"a":580},"navigate-replace-vs-push","When should you use navigate with replace: true versus a normal push?","A normal `navigate(path)` **pushes** a new entry onto the history stack, so the user can press Back to return. `navigate(path, { replace: true })` **replaces** the current entry, removing it from the stack so Back skips it.\n\n```jsx\nimport { useNavigate } from 'react-router-dom';\n\nfunction CheckoutSuccess() {\n  const navigate = useNavigate();\n\n  \u002F\u002F After payment, replace history so pressing Back\n  \u002F\u002F doesn't return to the payment form\n  useEffect(() => {\n    navigate('\u002Forder-confirmation', { replace: true });\n  }, []);\n\n  return null;\n}\n\n\u002F\u002F Also useful for auth redirects:\n\u002F\u002F navigate('\u002Flogin', { replace: true });\n\u002F\u002F — user can't go \"back\" to the protected page after logout\n```\n\n**Rule of thumb:** Use `replace: true` after any action that should be \"final\" in the history stack — payment success, login\u002Flogout, and post-submission confirmations are the standard cases.\n",{"id":582,"difficulty":96,"q":583,"a":584},"link-to-prop-types","What values can you pass to the \"to\" prop of Link?","The `to` prop accepts a **string** (path, with optional query\u002Fhash), a **partial location object**, or a **relative string**. Relative paths are resolved against the current route's URL segment, making them useful inside nested route trees.\n\n```jsx\nimport { Link } from 'react-router-dom';\n\nfunction Examples() {\n  return (\n    \u003C>\n      {\u002F* absolute string *\u002F}\n      \u003CLink to=\"\u002Fusers\">Users\u003C\u002FLink>\n\n      {\u002F* string with query + hash *\u002F}\n      \u003CLink to=\"\u002Fsearch?q=react#results\">Search\u003C\u002FLink>\n\n      {\u002F* location object for programmatic control *\u002F}\n      \u003CLink to={{ pathname: '\u002Fusers', search: '?page=2' }}>Page 2\u003C\u002FLink>\n\n      {\u002F* relative — goes up one segment then to \"edit\" *\u002F}\n      \u003CLink to=\"..\u002Fedit\">Edit\u003C\u002FLink>\n    \u003C\u002F>\n  );\n}\n```\n\n**Rule of thumb:** Prefer string `to` values for simplicity; switch to the object form only when you need to set `search`, `hash`, or `state` independently.\n",{"id":586,"difficulty":96,"q":587,"a":588},"routes-outside-router","What error do you get when you use a React Router hook outside of a Router, and how do you fix it?","Any React Router hook (`useNavigate`, `useLocation`, `useParams`, etc.) throws **\"useNavigate() may be used only in the context of a Router component\"** (or similar) when called outside the `BrowserRouter` \u002F `RouterProvider` tree. The fix is always to ensure the Router wraps the component tree at or above the point where the hook is called.\n\n```jsx\n\u002F\u002F WRONG — hook runs outside the Router tree\nfunction App() {\n  const navigate = useNavigate(); \u002F\u002F throws!\n  return \u003CBrowserRouter>...\u003C\u002FBrowserRouter>;\n}\n\n\u002F\u002F CORRECT — Router wraps everything that uses routing hooks\nfunction Root() {\n  return (\n    \u003CBrowserRouter>\n      \u003CApp \u002F>  {\u002F* App and all descendants can now use hooks *\u002F}\n    \u003C\u002FBrowserRouter>\n  );\n}\n\nfunction App() {\n  const navigate = useNavigate(); \u002F\u002F works fine here\n  return \u003CRoutes>...\u003C\u002FRoutes>;\n}\n```\n\n**Rule of thumb:** If you see a Router context error, trace the component tree upward — the component calling the hook is not a descendant of any `BrowserRouter` or `RouterProvider`.\n",20,{"description":94},"React Router v6 routing basics interview questions — BrowserRouter, Route, Link, NavLink, Outlet, nested routes, index routes, useNavigate, and catch-all 404 routes.","react\u002Frouting\u002Frouting-basics","aWZVzujf-dZeSv_aUKLbBl_U0vburev-ArANL2qZ1MU",{"id":595,"title":596,"body":597,"description":94,"difficulty":96,"extension":97,"framework":10,"frameworkSlug":8,"meta":601,"navigation":100,"order":13,"path":603,"questions":604,"questionsCount":168,"related":169,"seo":669,"seoDescription":670,"stem":671,"subtopic":672,"topic":36,"topicSlug":38,"updated":420,"__hash__":673},"qa\u002Freact\u002Fstate-and-data-flow\u002Flifting-state.md","Lifting State",{"type":91,"value":598,"toc":599},[],{"title":94,"searchDepth":11,"depth":11,"links":600},[],{"subtopicSlug":602},"lifting-state","\u002Freact\u002Fstate-and-data-flow\u002Flifting-state",[605,609,613,617,621,625,629,633,637,641,645,649,653,657,661,665],{"id":606,"difficulty":96,"q":607,"a":608},"lifting-state-what","What does \"lifting state up\" mean in React?","**Lifting state up** means moving shared state to the **lowest common\nancestor** of the components that need it. The ancestor owns the state\nand passes both the value and a setter callback down as props, so siblings\ncan read the same data and trigger updates through a common parent.\n\n```jsx\nfunction Parent() {\n  const [value, setValue] = useState('')\n\n  return (\n    \u003C>\n      \u003CInput value={value} onChange={setValue} \u002F>\n      \u003CPreview value={value} \u002F>\n    \u003C\u002F>\n  )\n}\n\n\u002F\u002F Input writes, Preview reads — state lives in Parent\n```\n\n**Rule of thumb:** If two components need to stay in sync, lift their\nshared state to the closest ancestor that contains both of them.\n",{"id":610,"difficulty":96,"q":611,"a":612},"lifting-state-why","Why is lifting state up necessary instead of each component keeping its own copy?","React enforces **one-way data flow** — a child cannot directly read or\nwrite a sibling's state. If two components each hold their own copy of\nthe same value, those copies can diverge and the UI becomes inconsistent.\n\n```jsx\n\u002F\u002F ❌ Two independent copies — can get out of sync\nfunction A() { const [x, setX] = useState(0); ... }\nfunction B() { const [x, setX] = useState(0); ... }\n\n\u002F\u002F ✅ One source of truth in the common parent\nfunction Parent() {\n  const [x, setX] = useState(0)\n  return \u003C>\u003CA x={x} onChange={setX} \u002F>\u003CB x={x} \u002F>\u003C\u002F>\n}\n```\n\nA single source of truth makes behaviour predictable: any change flows\ndown from the owner and every consumer automatically re-renders with\nthe latest value.\n\n**Rule of thumb:** If you ever copy state from props into another\n`useState`, you almost certainly have a sync problem waiting to happen.\n",{"id":614,"difficulty":96,"q":615,"a":616},"lifting-state-callback","How does a child component communicate a change back to its parent?","The parent passes a **callback prop** (e.g. `onChange`, `onSubmit`) to\nthe child. When the user interacts, the child calls that callback with\nthe new value. The parent's setter updates state, which flows back down.\n\n```jsx\nfunction Parent() {\n  const [qty, setQty] = useState(1)\n  return \u003CQuantityInput value={qty} onChange={setQty} \u002F>\n}\n\nfunction QuantityInput({ value, onChange }) {\n  return (\n    \u003Cinput\n      type=\"number\"\n      value={value}\n      onChange={e => onChange(Number(e.target.value))}\n    \u002F>\n  )\n}\n```\n\nThe child owns zero state here — it is a **controlled** component driven\nentirely by props.\n\n**Rule of thumb:** Name callbacks `onX` in the parent and `handleX`\ninside the child to keep the intent clear.\n",{"id":618,"difficulty":96,"q":619,"a":620},"lifting-state-siblings","Two sibling components need to share data. Where should the state live?","In their **closest common ancestor**. That ancestor becomes the single\nsource of truth and distributes both the value and update callbacks.\n\n```jsx\n\u002F\u002F Siblings: SearchBar and ResultsList need the same query string\nfunction SearchPage() {\n  const [query, setQuery] = useState('')\n\n  return (\n    \u003Cdiv>\n      \u003CSearchBar query={query} onSearch={setQuery} \u002F>\n      \u003CResultsList query={query} \u002F>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\nAvoid the temptation to let one sibling own the state and pass it\nsideways — React has no sibling-to-sibling channel.\n\n**Rule of thumb:** Siblings communicate via a shared parent, never\ndirectly.\n",{"id":622,"difficulty":121,"q":623,"a":624},"lifting-state-too-high","What goes wrong if you lift state higher than necessary?","Every component that receives the state as a prop will re-render when\nthat state changes — including ones that don't use it but happen to\nsit between the owner and the consumer. This causes **unnecessary\nre-renders** and can hurt performance in large trees.\n\n```jsx\n\u002F\u002F If `query` lives in App, every child of App re-renders on each\n\u002F\u002F keystroke — even Header, Sidebar, Footer that don't care about query\nfunction App() {\n  const [query, setQuery] = useState('')\n  return (\n    \u003C>\n      \u003CHeader \u002F>       {\u002F* re-renders needlessly *\u002F}\n      \u003CSidebar \u002F>      {\u002F* re-renders needlessly *\u002F}\n      \u003CSearchPage query={query} onSearch={setQuery} \u002F>\n    \u003C\u002F>\n  )\n}\n```\n\nKeep state as **low** in the tree as possible while still being shared\nwhere needed.\n\n**Rule of thumb:** Lift only as high as required; don't promote state\npre-emptively \"in case\" something else needs it.\n",{"id":626,"difficulty":121,"q":627,"a":628},"lifting-state-vs-context","When should you lift state vs. reach for Context or a global store?","| Scenario | Approach |\n|---|---|\n| 1–2 levels of passing, few consumers | Lift state (props) |\n| Deeply nested tree, many consumers, changes infrequently | Context |\n| Frequently updated, many subscribers, complex logic | External store (Redux, Zustand) |\n\nLifted state is the **simplest** solution and should be the default.\nContext adds an implicit dependency and can cause broad re-renders.\nA global store adds a dependency and operational complexity.\n\n```jsx\n\u002F\u002F Fine with lifting (shallow):\n\u003CPage>\u003CFilterBar \u002F>\u003CTable \u002F>\u003C\u002FPage>\n\n\u002F\u002F Consider Context (deeply nested):\n\u003CApp>\u003CLayout>\u003CSidebar>\u003CDeepWidget \u002F>\u003C\u002FSidebar>\u003C\u002FLayout>\u003C\u002FApp>\n```\n\n**Rule of thumb:** Start with lifting. Reach for Context only when prop\ndrilling becomes painful; reach for a store only when Context causes\nperformance problems.\n",{"id":630,"difficulty":96,"q":631,"a":632},"lifting-state-controlled","What is the relationship between lifting state and controlled components?","When you lift state into a parent and pass `value` + `onChange` to an\ninput, that input becomes a **controlled component** — the parent's\nstate is the single source of truth for the field's current value.\n\n```jsx\n\u002F\u002F Parent owns the state → input is controlled\nfunction Form() {\n  const [email, setEmail] = useState('')\n  return (\n    \u003Cinput\n      value={email}                         \u002F\u002F controlled\n      onChange={e => setEmail(e.target.value)}\n    \u002F>\n  )\n}\n```\n\nWithout lifting, each input would manage its own uncontrolled state\n(the DOM), making it harder for the parent to read or validate values.\n\n**Rule of thumb:** Forms almost always need lifted, controlled state so\nthe parent can validate and submit the combined field values.\n",{"id":634,"difficulty":121,"q":635,"a":636},"lifting-state-derived","What is derived state, and how does it relate to lifting?","**Derived state** is any value you can compute from existing state or\nprops. It should **not** be duplicated in a second `useState` — compute\nit inline during render instead.\n\n```jsx\n\u002F\u002F ❌ Redundant state — fullName can diverge\nconst [firstName, setFirstName] = useState('')\nconst [lastName, setLastName] = useState('')\nconst [fullName, setFullName] = useState('')   \u002F\u002F duplicated!\n\n\u002F\u002F ✅ Derive it — always in sync, zero extra state\nconst fullName = `${firstName} ${lastName}`\n```\n\nLifting state and avoiding derived copies are two sides of the same\ncoin: both enforce a **single source of truth**.\n\n**Rule of thumb:** If a value can be computed from existing state, don't\nstore it separately — just compute it.\n",{"id":638,"difficulty":121,"q":639,"a":640},"lifting-state-performance","How can you prevent child re-renders caused by callback props passed from a parent?","Wrap the callback in `useCallback` so the function reference is stable\nbetween renders. Pair it with `React.memo` on the child to skip\nre-renders when neither value nor callback changed.\n\n```jsx\nfunction Parent() {\n  const [count, setCount] = useState(0)\n\n  const handleChange = useCallback((val) => {\n    setCount(val)\n  }, [])                  \u002F\u002F stable reference\n\n  return \u003CExpensiveChild onChange={handleChange} count={count} \u002F>\n}\n\nconst ExpensiveChild = React.memo(({ count, onChange }) => {\n  \u002F\u002F only re-renders when count or onChange actually changes\n  return \u003Cinput value={count} onChange={e => onChange(Number(e.target.value))} \u002F>\n})\n```\n\nWithout `useCallback`, a new function is created every render, breaking\n`React.memo`'s shallow equality check.\n\n**Rule of thumb:** `useCallback` is most valuable when the callback is\npassed to a memoized child — otherwise it's premature optimisation.\n",{"id":642,"difficulty":121,"q":643,"a":644},"lifting-state-multiple","When multiple pieces of related state need to be lifted, should they be separate useState calls or one object?","Group them in **one object** (or `useReducer`) when they change together\nor are conceptually coupled. Keep them separate when they change\nindependently.\n\n```jsx\n\u002F\u002F ✅ Coupled — lift as one object\nconst [position, setPosition] = useState({ x: 0, y: 0 })\n\u002F\u002F Update: setPosition(prev => ({ ...prev, x: newX }))\n\n\u002F\u002F ✅ Independent — separate calls\nconst [isOpen, setIsOpen] = useState(false)\nconst [query, setQuery]   = useState('')\n```\n\nGrouping reduces the number of props you thread through the tree;\nkeeping them separate avoids unnecessary object spread on every update.\n\n**Rule of thumb:** If you always update both values at the same time,\ngroup them; if you update them independently, keep them separate.\n",{"id":646,"difficulty":121,"q":647,"a":648},"lifting-state-initialiser","How do you initialise lifted state from a prop only once without re-syncing on every render?","Pass the initial value as the `useState` initialiser argument. React\nonly evaluates it on the **first render** — subsequent prop changes are\nintentionally ignored because the state is now owned by the parent.\n\n```jsx\n\u002F\u002F ❌ Anti-pattern: syncing prop to state on every render\nfunction Child({ initialCount }) {\n  const [count, setCount] = useState(initialCount)\n  useEffect(() => { setCount(initialCount) }, [initialCount])\n  \u002F\u002F now state and prop fight for ownership\n}\n\n\u002F\u002F ✅ Use initialValue convention — document that updates won't flow down\nfunction Counter({ initialCount }) {\n  const [count, setCount] = useState(initialCount)  \u002F\u002F first render only\n  return \u003Cbutton onClick={() => setCount(c => c + 1)}>{count}\u003C\u002Fbutton>\n}\n```\n\nIf ongoing sync is required, the parent should own the state and pass a\ncontrolled value + callback instead.\n\n**Rule of thumb:** Prefix props that seed state only once with\n`initial` (`initialCount`, `initialValue`) to signal they're not\nkept in sync.\n",{"id":650,"difficulty":96,"q":651,"a":652},"lifting-state-handler-naming","What naming convention is commonly used for event-handler props when lifting state?","Use `onX` for props that accept a callback and `handleX` for the\nfunction itself — mirroring React's own `onClick`, `onChange`, etc.\n\n```jsx\n\u002F\u002F Parent — owns state, names the prop onSearch\nfunction SearchPage() {\n  const [query, setQuery] = useState('')\n  function handleSearch(q) { setQuery(q) }\n\n  return \u003CSearchBar onSearch={handleSearch} \u002F>\n}\n\n\u002F\u002F Child — receives the prop, calls it when the user acts\nfunction SearchBar({ onSearch }) {\n  return \u003Cinput onChange={e => onSearch(e.target.value)} \u002F>\n}\n```\n\nConsistent naming makes it immediately obvious at the call site which\nprop triggers parent behaviour.\n\n**Rule of thumb:** `on` prefix = prop the caller provides;\n`handle` prefix = the local function that implements it.\n",{"id":654,"difficulty":96,"q":655,"a":656},"lifting-state-unidirectional","What is \"unidirectional data flow\" and why does React enforce it?","**Unidirectional data flow** means data travels in one direction only:\n**down** the tree via props. A child can signal a change (via callback),\nbut the parent decides whether and how state updates, then the new value\nflows back down.\n\n```\nParent state\n     ↓ props\n  Child A     Child B\n     ↑ callback (event)\n```\n\nThis makes the state transitions explicit, predictable, and easy to\ndebug — you always know where a piece of data lives and who can change\nit.\n\n**Rule of thumb:** If you find yourself needing to pass data *upward*\nwithout a callback, that's a sign the state should live higher or in\na shared store.\n",{"id":658,"difficulty":121,"q":659,"a":660},"lifting-state-anti-pattern-sync","Why is copying parent state into child state considered an anti-pattern?","When you copy a prop into `useState`, you create two sources of truth.\nIf the parent updates the prop, the child's copy stays stale unless you\nadd a `useEffect` to sync — which introduces complexity and can cause\none-render lag bugs.\n\n```jsx\n\u002F\u002F ❌ Derived state anti-pattern\nfunction Child({ value }) {\n  const [localValue, setLocalValue] = useState(value)\n  \u002F\u002F If parent changes `value`, localValue is now stale\n  useEffect(() => setLocalValue(value), [value]) \u002F\u002F band-aid\n}\n\n\u002F\u002F ✅ Just use the prop directly\nfunction Child({ value, onChange }) {\n  return \u003Cinput value={value} onChange={e => onChange(e.target.value)} \u002F>\n}\n```\n\nThe key insight: if a component should be driven by the parent, make\nit **fully controlled** — no local copy.\n\n**Rule of thumb:** Before writing `useState(someProp)`, ask whether the\nchild really needs to own that state. Usually the answer is no.\n",{"id":662,"difficulty":121,"q":663,"a":664},"lifting-state-form-submit","How does lifting state enable a parent to collect and submit form field values?","With lifted state, the parent holds every field value and can read them\nall in the submit handler without querying the DOM.\n\n```jsx\nfunction SignupForm() {\n  const [form, setForm] = useState({ name: '', email: '' })\n\n  function handleChange(field, value) {\n    setForm(prev => ({ ...prev, [field]: value }))\n  }\n\n  function handleSubmit(e) {\n    e.preventDefault()\n    api.signup(form)   \u002F\u002F all values available here\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003CTextInput label=\"Name\"  value={form.name}  onChange={v => handleChange('name', v)} \u002F>\n      \u003CTextInput label=\"Email\" value={form.email} onChange={v => handleChange('email', v)} \u002F>\n      \u003Cbutton type=\"submit\">Sign up\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n**Rule of thumb:** Lift all form fields into the parent that owns the\nsubmit action so validation and submission see a consistent snapshot.\n",{"id":666,"difficulty":208,"q":667,"a":668},"lifting-state-list-items","How do you handle lifted state for a dynamic list where each item has its own editable fields?","Store the list in the parent as an array of objects. Pass each item and\nan `onChange` callback that identifies the item by index or `id`.\n\n```jsx\nfunction TodoList() {\n  const [todos, setTodos] = useState([\n    { id: 1, text: 'Buy milk', done: false },\n    { id: 2, text: 'Walk dog', done: false },\n  ])\n\n  function handleToggle(id) {\n    setTodos(prev =>\n      prev.map(t => t.id === id ? { ...t, done: !t.done } : t)\n    )\n  }\n\n  return todos.map(todo => (\n    \u003CTodoItem key={todo.id} todo={todo} onToggle={handleToggle} \u002F>\n  ))\n}\n\nfunction TodoItem({ todo, onToggle }) {\n  return (\n    \u003Clabel>\n      \u003Cinput type=\"checkbox\" checked={todo.done}\n             onChange={() => onToggle(todo.id)} \u002F>\n      {todo.text}\n    \u003C\u002Flabel>\n  )\n}\n```\n\nThe parent is the single source of truth for the entire list; each\nchild is a controlled component.\n\n**Rule of thumb:** Always identify list items by a stable `id` (not\nindex) when items can be added, removed, or reordered.\n",{"description":94},"React lifting state interview questions — single source of truth, callback props, sibling communication, when to lift vs. use a store.","react\u002Fstate-and-data-flow\u002Flifting-state","Lifting State Up","crdExT6WcsvjkdZN2V3ZRKAnaBpWm4wpjIfDlOEHUss",{"id":675,"title":676,"body":677,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":681,"navigation":100,"order":13,"path":682,"questions":683,"questionsCount":416,"related":169,"seo":760,"seoDescription":761,"stem":762,"subtopic":676,"topic":63,"topicSlug":65,"updated":420,"__hash__":763},"qa\u002Freact\u002Fstate-management\u002Fredux-toolkit.md","Redux Toolkit",{"type":91,"value":678,"toc":679},[],{"title":94,"searchDepth":11,"depth":11,"links":680},[],{},"\u002Freact\u002Fstate-management\u002Fredux-toolkit",[684,688,692,696,700,704,708,712,716,720,724,728,732,736,740,744,748,752,756],{"id":685,"difficulty":96,"q":686,"a":687},"what-problem-rtk-solves","What problems does Redux Toolkit solve compared to plain Redux?","Plain Redux requires a lot of **boilerplate**: action type constants, action\ncreator functions, hand-written immutable updates in reducers, and manual\nmiddleware wiring. Every new feature duplicates this ceremony, leading to\nsprawling files and easy mistakes (e.g., accidentally mutating state).\n\n**Redux Toolkit (RTK)** eliminates this with three design decisions: it\nbundles **Immer** so you write mutating-looking reducer code that is\nactually immutable under the hood; it provides **`createSlice`** which\nauto-generates action creators and action types from a reducer map; and\n**`configureStore`** sets up Redux DevTools and `redux-thunk` middleware\nautomatically.\n\n```js\n\u002F\u002F Plain Redux — three files of ceremony for one counter\nconst INCREMENT = 'counter\u002Fincrement'\nconst increment = () => ({ type: INCREMENT })\nfunction reducer(state = { value: 0 }, action) {\n  switch (action.type) {\n    case INCREMENT: return { ...state, value: state.value + 1 } \u002F\u002F manual spread\n    default: return state\n  }\n}\n\n\u002F\u002F RTK — everything above in ~5 lines\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) { state.value++ } \u002F\u002F Immer handles immutability\n  }\n})\n```\n\n**Rule of thumb:** If you're writing `...state` spreads or a `types.js`\nconstants file, you should be using Redux Toolkit instead.\n",{"id":689,"difficulty":96,"q":690,"a":691},"configure-store-setup","How do you set up a Redux store with configureStore?","**`configureStore`** is RTK's store factory. It accepts a `reducer` map\n(each key becomes a top-level slice of state), and it automatically\nenables the **Redux DevTools Extension** and adds **`redux-thunk`**\nmiddleware — no manual `compose` or `applyMiddleware` needed.\n\n```js\nimport { configureStore } from '@reduxjs\u002Ftoolkit'\nimport counterReducer from '.\u002Ffeatures\u002Fcounter\u002FcounterSlice'\nimport userReducer from '.\u002Ffeatures\u002Fuser\u002FuserSlice'\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterReducer, \u002F\u002F state.counter\n    user: userReducer,       \u002F\u002F state.user\n  },\n  \u002F\u002F middleware and devTools are auto-configured; override only when needed\n})\n\n\u002F\u002F Infer RootState and AppDispatch types (TypeScript)\nexport type RootState = ReturnType\u003Ctypeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n```\n\nWrap your app in `\u003CProvider store={store}>` from `react-redux` so every\ncomponent can access the store via hooks.\n\n**Rule of thumb:** One `configureStore` call per app; export `RootState`\nand `AppDispatch` from the same file so TypeScript consumers stay in sync.\n",{"id":693,"difficulty":96,"q":694,"a":695},"create-slice-basics","What does createSlice do and what does it return?","**`createSlice`** takes a name, an initial state, and a `reducers` object.\nIt returns a **slice object** containing the generated reducer function and\nan `actions` object whose keys match the reducer names.\n\n```js\nimport { createSlice } from '@reduxjs\u002Ftoolkit'\n\nconst counterSlice = createSlice({\n  name: 'counter',            \u002F\u002F prefix for action types: 'counter\u002Fincrement'\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) { state.value++ },           \u002F\u002F action: counterSlice.actions.increment\n    decrement(state) { state.value-- },\n    incrementBy(state, action) {\n      state.value += action.payload               \u002F\u002F payload is the argument passed to the action creator\n    },\n  },\n})\n\nexport const { increment, decrement, incrementBy } = counterSlice.actions\nexport default counterSlice.reducer               \u002F\u002F plug into configureStore\n```\n\nThe action type strings are auto-namespaced as `\"sliceName\u002FreducerName\"`,\nwhich keeps them unique across a large app without a constants file.\n\n**Rule of thumb:** One `createSlice` per feature; export named action\ncreators and the default reducer from the same file.\n",{"id":697,"difficulty":96,"q":698,"a":699},"immer-mutations","How does Immer make Redux reducers easier to write?","RTK's reducers run inside **Immer's `produce`** function. Immer gives you\na **draft proxy** of the current state. Any mutations you apply to the\ndraft are recorded and used to create a new immutable state object —\nyour original state is never changed.\n\n```js\nreducers: {\n  addTodo(state, action) {\n    state.items.push(action.payload)  \u002F\u002F looks like mutation, but Immer produces a new array\n  },\n  removeTodo(state, action) {\n    const index = state.items.findIndex(t => t.id === action.payload)\n    if (index !== -1) state.items.splice(index, 1) \u002F\u002F splice on the draft is safe\n  },\n  updateTodo(state, action) {\n    const todo = state.items.find(t => t.id === action.payload.id)\n    if (todo) todo.text = action.payload.text     \u002F\u002F direct property assignment is fine\n  },\n}\n```\n\nThe one rule: you must either **mutate the draft** OR **return a new\nvalue** — never both in the same reducer case. Returning `undefined`\nimplicitly keeps the current draft.\n\n**Rule of thumb:** Mutate the draft for nested updates; return a\nreplacement value only when you want to replace the entire state slice.\n",{"id":701,"difficulty":96,"q":702,"a":703},"use-selector-use-dispatch","How do useSelector and useDispatch connect components to the Redux store?","**`useSelector`** reads a value from the Redux store. It accepts a\nselector function that receives the full `RootState` and returns the\nvalue you need. The component re-renders only when that value changes.\n**`useDispatch`** returns the store's dispatch function so you can send\nactions.\n\n```jsx\nimport { useSelector, useDispatch } from 'react-redux'\nimport { increment, incrementBy } from '.\u002FcounterSlice'\n\nfunction Counter() {\n  const count = useSelector(state => state.counter.value) \u002F\u002F subscribe to one slice\n  const dispatch = useDispatch()\n\n  return (\n    \u003Cdiv>\n      \u003Cp>{count}\u003C\u002Fp>\n      \u003Cbutton onClick={() => dispatch(increment())}>+1\u003C\u002Fbutton>\n      \u003Cbutton onClick={() => dispatch(incrementBy(5))}>+5\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\nIn TypeScript, use typed wrappers (`useAppSelector`, `useAppDispatch`)\nderived from `RootState` and `AppDispatch` so you get full autocomplete\nwithout repeating type annotations in every component.\n\n**Rule of thumb:** Keep selector functions simple in the component; move\ncomplex derived logic to standalone selector functions or `createSelector`.\n",{"id":705,"difficulty":121,"q":706,"a":707},"create-async-thunk","How does createAsyncThunk handle asynchronous actions?","**`createAsyncThunk`** wraps an async function and automatically dispatches\nthree lifecycle actions: `pending`, `fulfilled`, and `rejected`. You handle\nthese in `extraReducers` to update loading\u002Ferror\u002Fdata state.\n\n```js\nimport { createAsyncThunk, createSlice } from '@reduxjs\u002Ftoolkit'\n\n\u002F\u002F First arg: action type prefix; second arg: async payload creator\nexport const fetchUser = createAsyncThunk('user\u002FfetchById', async (userId) => {\n  const res = await fetch(`\u002Fapi\u002Fusers\u002F${userId}`)\n  if (!res.ok) throw new Error('Not found')   \u002F\u002F throw to trigger rejected\n  return res.json()                             \u002F\u002F returned value becomes action.payload in fulfilled\n})\n\nconst userSlice = createSlice({\n  name: 'user',\n  initialState: { data: null, status: 'idle', error: null },\n  reducers: {},\n  extraReducers(builder) {\n    builder\n      .addCase(fetchUser.pending,   (state) => { state.status = 'loading' })\n      .addCase(fetchUser.fulfilled, (state, action) => {\n        state.status = 'succeeded'\n        state.data = action.payload             \u002F\u002F Immer draft mutation\n      })\n      .addCase(fetchUser.rejected,  (state, action) => {\n        state.status = 'failed'\n        state.error = action.error.message\n      })\n  },\n})\n```\n\nDispatch it like any action: `dispatch(fetchUser(42))`. The thunk returns\na promise you can `await` and `.unwrap()` to re-throw errors into a\ntry\u002Fcatch in the component.\n\n**Rule of thumb:** Use `createAsyncThunk` for one-off fetches; switch to\nRTK Query when you need caching, deduplication, or automatic refetching.\n",{"id":709,"difficulty":121,"q":710,"a":711},"loading-error-state-pattern","What is the standard pattern for tracking loading and error state with createAsyncThunk?","The convention is to keep a **`status`** field (`'idle' | 'loading' |\n'succeeded' | 'failed'`) alongside an **`error`** field in the slice's\ninitial state. Each lifecycle case updates these fields atomically with\nthe data.\n\n```js\nconst initialState = {\n  items: [],\n  status: 'idle',    \u002F\u002F drives skeleton\u002Fspinner UI\n  error: null,       \u002F\u002F drives error banner UI\n}\n\nextraReducers(builder) {\n  builder\n    .addCase(fetchItems.pending, (state) => {\n      state.status = 'loading'\n      state.error = null              \u002F\u002F clear previous error on retry\n    })\n    .addCase(fetchItems.fulfilled, (state, action) => {\n      state.status = 'succeeded'\n      state.items = action.payload\n    })\n    .addCase(fetchItems.rejected, (state, action) => {\n      state.status = 'failed'\n      state.error = action.error.message ?? 'Unknown error'\n    })\n}\n```\n\nIn the component, read `status` with `useSelector` and branch:\n`if (status === 'loading') return \u003CSpinner \u002F>`. Reset `status` to `'idle'`\nwhen navigating away so the next mount triggers a fresh fetch.\n\n**Rule of thumb:** Never use a boolean `isLoading` flag — a string `status`\nfield handles all four states without impossible combinations.\n",{"id":713,"difficulty":121,"q":714,"a":715},"rtk-query-overview","What is RTK Query and how does it differ from createAsyncThunk?","**RTK Query** is a data-fetching and caching layer built into RTK. It\ngenerates hooks, manages a normalized request cache, handles\ndeduplication, and supports automatic background refetching — things you\nwould have to build manually with `createAsyncThunk`.\n\n```js\nimport { createApi, fetchBaseQuery } from '@reduxjs\u002Ftoolkit\u002Fquery\u002Freact'\n\nexport const postsApi = createApi({\n  reducerPath: 'postsApi',              \u002F\u002F key in the Redux state\n  baseQuery: fetchBaseQuery({ baseUrl: '\u002Fapi' }),\n  endpoints: (builder) => ({\n    getPosts: builder.query({\n      query: () => '\u002Fposts',            \u002F\u002F appended to baseUrl\n    }),\n    getPostById: builder.query({\n      query: (id) => `\u002Fposts\u002F${id}`,\n    }),\n  }),\n})\n\n\u002F\u002F Auto-generated hooks follow the pattern: use\u003CEndpointName>Query\nexport const { useGetPostsQuery, useGetPostByIdQuery } = postsApi\n```\n\nAdd `postsApi.reducer` to `configureStore` and\n`postsApi.middleware` to the middleware chain. The hooks return\n`{ data, isLoading, isError }` — no manual `extraReducers` needed.\n\n**Rule of thumb:** Default to RTK Query for server data; use\n`createAsyncThunk` only for non-idempotent mutations that don't fit a\nREST endpoint pattern.\n",{"id":717,"difficulty":208,"q":718,"a":719},"rtk-query-cache-invalidation","How does RTK Query handle cache invalidation with tags?","RTK Query uses a **tag** system to express which queries are invalidated\nwhen a mutation runs. A query **provides** tags; a mutation **invalidates**\nthose tags, causing every matching query to refetch.\n\n```js\nexport const postsApi = createApi({\n  reducerPath: 'postsApi',\n  baseQuery: fetchBaseQuery({ baseUrl: '\u002Fapi' }),\n  tagTypes: ['Post'],                          \u002F\u002F register tag names\n  endpoints: (builder) => ({\n    getPosts: builder.query({\n      query: () => '\u002Fposts',\n      providesTags: ['Post'],                  \u002F\u002F this cache entry is tagged 'Post'\n    }),\n    getPostById: builder.query({\n      query: (id) => `\u002Fposts\u002F${id}`,\n      providesTags: (result, error, id) => [{ type: 'Post', id }], \u002F\u002F fine-grained tag\n    }),\n    createPost: builder.mutation({\n      query: (body) => ({ url: '\u002Fposts', method: 'POST', body }),\n      invalidatesTags: ['Post'],               \u002F\u002F bust all 'Post' queries on success\n    }),\n    updatePost: builder.mutation({\n      query: ({ id, ...patch }) => ({ url: `\u002Fposts\u002F${id}`, method: 'PATCH', body: patch }),\n      invalidatesTags: (result, error, { id }) => [{ type: 'Post', id }], \u002F\u002F only bust this post\n    }),\n  }),\n})\n```\n\nWhen `updatePost` succeeds, only the `getPostById` query for that specific\n`id` refetches, leaving other cached posts untouched.\n\n**Rule of thumb:** Use list tags (e.g., `'Post'`) to bust the whole\ncollection; use entity tags (`{ type: 'Post', id }`) to bust individual\nitems — mix both in `createPost` to refresh the list and the detail.\n",{"id":721,"difficulty":121,"q":722,"a":723},"rtk-query-hooks-usage","How do you use RTK Query hooks in a component?","The auto-generated **`useGetXQuery`** hooks subscribe the component to the\ncached data. They return a result object with `data`, `isLoading`,\n`isFetching`, `isError`, and `error` properties.\n\n```jsx\nimport { useGetPostsQuery } from '.\u002FpostsApi'\n\nfunction PostsList() {\n  const {\n    data: posts = [],   \u002F\u002F default to [] to avoid null checks on first render\n    isLoading,\n    isError,\n    error,\n    refetch,            \u002F\u002F manually trigger a refetch\n  } = useGetPostsQuery()\n\n  if (isLoading) return \u003Cp>Loading…\u003C\u002Fp>\n  if (isError)   return \u003Cp>Error: {error.message}\u003C\u002Fp>\n\n  return (\n    \u003Cul>\n      {posts.map(post => \u003Cli key={post.id}>{post.title}\u003C\u002Fli>)}\n    \u003C\u002Ful>\n  )\n}\n```\n\nFor mutations, use the auto-generated `useXMutation` hook which returns\na `[trigger, result]` tuple — call `trigger(args)` to fire the request.\n\n**Rule of thumb:** Prefer `isLoading` (true only on first fetch) over\n`isFetching` (true on every fetch including background refetches) when\nrendering the initial skeleton.\n",{"id":725,"difficulty":96,"q":726,"a":727},"redux-devtools","How does Redux DevTools work with RTK and what can you inspect?","**Redux DevTools** is a browser extension that `configureStore` enables\nautomatically in development (no extra config needed). It connects to the\nstore and records every action dispatched along with a before\u002Fafter state\nsnapshot.\n\n```js\n\u002F\u002F configureStore enables DevTools automatically; disable in prod if needed\nexport const store = configureStore({\n  reducer: rootReducer,\n  devTools: process.env.NODE_ENV !== 'production', \u002F\u002F explicit control\n})\n```\n\nWith the extension open you can: **time-travel** (jump to any past state),\n**replay** the action log after a hot reload, **inspect** the diff between\nstates, and **dispatch** actions manually from the DevTools panel to test\nreducers without touching the UI.\n\nRTK also serializes the `createAsyncThunk` lifecycle actions so you see\n`user\u002FfetchById\u002Fpending`, `user\u002FfetchById\u002Ffulfilled`, etc., in the log.\n\n**Rule of thumb:** Keep action payloads serializable (plain objects, no\nclass instances, no functions) so DevTools can display and replay them\naccurately.\n",{"id":729,"difficulty":208,"q":730,"a":731},"create-entity-adapter","What is createEntityAdapter and when should you use it?","**`createEntityAdapter`** provides a standardized way to store a\ncollection of items by their ID. It creates an **`{ ids: [], entities: {} }`**\nnormalized shape and generates CRUD reducer helpers (`addOne`, `upsertMany`,\n`removeOne`, etc.).\n\n```js\nimport { createEntityAdapter, createSlice } from '@reduxjs\u002Ftoolkit'\n\nconst todosAdapter = createEntityAdapter()\n\u002F\u002F { selectAll, selectById, selectIds, selectEntities, selectTotal }\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: todosAdapter.getInitialState({ status: 'idle' }), \u002F\u002F adds ids\u002Fentities keys\n  reducers: {\n    todoAdded:   todosAdapter.addOne,         \u002F\u002F pre-built reducer\n    todosLoaded: todosAdapter.setAll,\n    todoRemoved: todosAdapter.removeOne,\n  },\n  extraReducers(builder) {\n    builder.addCase(fetchTodos.fulfilled, (state, action) => {\n      todosAdapter.setAll(state, action.payload) \u002F\u002F bulk replace\n    })\n  },\n})\n\n\u002F\u002F Selectors are pre-built and accept RootState\nexport const { selectAll: selectAllTodos, selectById: selectTodoById } =\n  todosAdapter.getSelectors(state => state.todos)\n```\n\nNormalize when you have a large list and need O(1) lookups by ID (e.g.,\nselecting one item without scanning an array).\n\n**Rule of thumb:** Use `createEntityAdapter` whenever you fetch a list\nthat you later update by ID; skip it for small static lists.\n",{"id":733,"difficulty":121,"q":734,"a":735},"memoized-selectors","How do you create memoized selectors with createSelector?","**`createSelector`** (re-exported from `reselect` by RTK) takes one or\nmore **input selectors** and a **result function**. The result is\nrecomputed only when an input selector's output changes, preventing\nunnecessary re-renders from derived computations.\n\n```js\nimport { createSelector } from '@reduxjs\u002Ftoolkit'\n\nconst selectTodos = state => state.todos.items          \u002F\u002F input selector\nconst selectFilter = state => state.todos.filter        \u002F\u002F input selector\n\n\u002F\u002F result function only runs when selectTodos or selectFilter changes\nexport const selectFilteredTodos = createSelector(\n  [selectTodos, selectFilter],\n  (todos, filter) => {\n    if (filter === 'all') return todos\n    return todos.filter(t => t.status === filter)       \u002F\u002F expensive work cached\n  }\n)\n```\n\nUse it inside `useSelector`: `const todos = useSelector(selectFilteredTodos)`.\nBecause the selector reference is stable across renders (the memoized\ninstance is defined outside the component), React-Redux's equality check\nworks correctly.\n\n**Rule of thumb:** Extract a `createSelector` call whenever the selector\ncomputes a new array or object reference — otherwise every call returns a\nnew reference and causes a re-render even when the data hasn't changed.\n",{"id":737,"difficulty":121,"q":738,"a":739},"middleware-thunk-custom","How do you add custom middleware to a Redux Toolkit store?","`configureStore` adds `redux-thunk` by default. To add custom middleware,\nuse the **`middleware`** callback option which receives `getDefaultMiddleware`\nso you can keep the defaults and append your own.\n\n```js\nimport { configureStore } from '@reduxjs\u002Ftoolkit'\n\nconst loggerMiddleware = store => next => action => {\n  console.log('dispatching', action)       \u002F\u002F called before reducer\n  const result = next(action)              \u002F\u002F pass action down the chain\n  console.log('next state', store.getState())\n  return result\n}\n\nexport const store = configureStore({\n  reducer: rootReducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware()               \u002F\u002F keeps thunk + serializability checks\n      .concat(loggerMiddleware),         \u002F\u002F append custom middleware\n})\n```\n\nDo not replace `getDefaultMiddleware()` with an empty array unless you\nintentionally want to remove the serialization warnings and thunk support —\nthose defaults catch common bugs.\n\n**Rule of thumb:** Always call `getDefaultMiddleware()` as the base;\n`prepend` for middleware that needs to run before thunks, `concat` for\neverything else.\n",{"id":741,"difficulty":121,"q":742,"a":743},"feature-folder-structure","What is the recommended way to structure Redux slices in a large app?","The **feature folder** (or \"ducks\") pattern co-locates everything related\nto one domain: the slice, thunks, selectors, and component files. This\nkeeps related code together and avoids cross-cutting files like\n`allReducers.js`.\n\n```\nsrc\u002F\n  features\u002F\n    cart\u002F\n      cartSlice.ts       \u002F\u002F createSlice + extraReducers\n      cartThunks.ts      \u002F\u002F createAsyncThunk (or inline in slice)\n      cartSelectors.ts   \u002F\u002F createSelector calls\n      CartPage.tsx       \u002F\u002F component that uses the slice\n      cartApi.ts         \u002F\u002F RTK Query createApi (if feature-scoped)\n    user\u002F\n      userSlice.ts\n      userSelectors.ts\n      ProfilePage.tsx\n  store\u002F\n    store.ts             \u002F\u002F configureStore — imports slice reducers\n    hooks.ts             \u002F\u002F typed useAppSelector \u002F useAppDispatch\n```\n\n```js\n\u002F\u002F store\u002Fhooks.ts — typed wrappers to avoid repeating types in every component\nimport { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'\nimport type { RootState, AppDispatch } from '.\u002Fstore'\nexport const useAppDispatch = () => useDispatch\u003CAppDispatch>()\nexport const useAppSelector: TypedUseSelectorHook\u003CRootState> = useSelector\n```\n\n**Rule of thumb:** Keep each feature self-contained; the `store.ts` file\nshould only import reducers — no business logic lives there.\n",{"id":745,"difficulty":121,"q":746,"a":747},"when-not-to-use-redux","When should you NOT use Redux for state management?","Redux shines for **shared, cross-component global state** with complex\nupdate logic. It is overkill — and adds friction — for state that fits\nsimpler tools.\n\nAvoid Redux for:\n- **Local UI state** (modal open, form input value, hover) — `useState` is\n  sufficient and keeps the logic next to the component.\n- **Server cache state** (fetched lists, single items) — RTK Query or React\n  Query handle caching, deduplication, and refetching better than a\n  hand-rolled async slice.\n- **Small apps with one team** — Context + `useReducer` may be all you\n  need; the cognitive overhead of slices\u002Fselectors\u002Factions is not always\n  worth it.\n\n```jsx\n\u002F\u002F BAD — Redux for toggle state no other component needs\ndispatch(setModalOpen(true))\n\n\u002F\u002F GOOD — local state for isolated UI\nconst [isOpen, setIsOpen] = useState(false)\n```\n\nSymptoms of unnecessary Redux: your slices hold `isModalOpen` flags,\nyou dispatch actions from a single component and no other component\nsubscribes, or you have more action types than actual data fields.\n\n**Rule of thumb:** If no other component needs to read or write a piece\nof state, keep it local with `useState` — move it to Redux only when the\nneed actually arises.\n",{"id":749,"difficulty":208,"q":750,"a":751},"prepare-callback","What is the prepare callback in createSlice and when do you use it?","By default, action creators generated by `createSlice` accept one\nargument which becomes `action.payload`. The **`prepare` callback** lets\nyou transform the arguments — generate IDs, add timestamps, or restructure\nthe payload — before the reducer runs.\n\n```js\nimport { createSlice, nanoid } from '@reduxjs\u002Ftoolkit'\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [],\n  reducers: {\n    todoAdded: {\n      reducer(state, action) {\n        state.push(action.payload)                \u002F\u002F payload already shaped\n      },\n      prepare(text) {                            \u002F\u002F called first with the caller's args\n        return {\n          payload: {\n            id: nanoid(),                        \u002F\u002F auto-generate a unique id\n            text,\n            createdAt: new Date().toISOString(), \u002F\u002F timestamp at dispatch time\n            completed: false,\n          },\n        }\n      },\n    },\n  },\n})\n\n\u002F\u002F Caller just passes the text — ID and timestamp are added automatically\ndispatch(todoAdded('Buy milk'))\n```\n\n**Rule of thumb:** Use `prepare` to keep side effects (ID generation,\ntimestamps) out of reducers and out of components — reducers must be\npure, and components shouldn't be responsible for shaping payloads.\n",{"id":753,"difficulty":121,"q":754,"a":755},"rtk-query-mutation","How do you perform a mutation with RTK Query and update the UI after it completes?","Mutations are defined with `builder.mutation` and exposed as\n**`useXMutation`** hooks. The hook returns a `[triggerFn, resultObject]`\ntuple. Call the trigger to fire the request; the result object tracks\nthe mutation's lifecycle.\n\n```jsx\nimport { useCreatePostMutation } from '.\u002FpostsApi'\n\nfunction NewPostForm() {\n  const [createPost, { isLoading, isError, isSuccess }] = useCreatePostMutation()\n  const [title, setTitle] = useState('')\n\n  async function handleSubmit(e) {\n    e.preventDefault()\n    try {\n      await createPost({ title }).unwrap() \u002F\u002F unwrap re-throws on error\n      setTitle('')                          \u002F\u002F clear form on success\n    } catch (err) {\n      console.error('Failed to save post', err)\n    }\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput value={title} onChange={e => setTitle(e.target.value)} \u002F>\n      \u003Cbutton disabled={isLoading}>\n        {isLoading ? 'Saving…' : 'Save'}\n      \u003C\u002Fbutton>\n      {isSuccess && \u003Cp>Saved!\u003C\u002Fp>}\n      {isError   && \u003Cp>Something went wrong.\u003C\u002Fp>}\n    \u003C\u002Fform>\n  )\n}\n```\n\nIf the mutation's `invalidatesTags` overlaps with a query's `providesTags`,\nthat query automatically refetches — no manual cache update needed.\n\n**Rule of thumb:** Always call `.unwrap()` in a try\u002Fcatch so thrown errors\nsurface in your component rather than being silently swallowed.\n",{"id":757,"difficulty":96,"q":758,"a":759},"action-type-strings","How are Redux action type strings structured in Redux Toolkit?","RTK automatically names action types as **`\"sliceName\u002FreducerName\"`** for\nslice actions, and **`\"actionPrefix\u002Fpending|fulfilled|rejected\"`** for\n`createAsyncThunk` lifecycle actions.\n\n```js\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) { state.value++ },\n  },\n})\n\ncounterSlice.actions.increment.type   \u002F\u002F 'counter\u002Fincrement'\ncounterSlice.actions.increment()      \u002F\u002F { type: 'counter\u002Fincrement' }\n\nconst fetchUser = createAsyncThunk('user\u002Ffetch', async (id) => { \u002F* ... *\u002F })\nfetchUser.pending.type    \u002F\u002F 'user\u002Ffetch\u002Fpending'\nfetchUser.fulfilled.type  \u002F\u002F 'user\u002Ffetch\u002Ffulfilled'\nfetchUser.rejected.type   \u002F\u002F 'user\u002Ffetch\u002Frejected'\n```\n\nBecause names are derived from the slice name, keep slice names\ndescriptive and scoped to their feature. Avoid generic names like\n`'data'` or `'state'` which will produce cryptic action logs in DevTools.\n\n**Rule of thumb:** The DevTools action log is your first debugging tool —\ntreat action type strings as documentation and name them clearly.\n",{"description":94},"Redux Toolkit interview questions — createSlice, configureStore, createAsyncThunk, RTK Query, immer mutations, and Redux DevTools for React state management.","react\u002Fstate-management\u002Fredux-toolkit","McrLS2seIz5b0yfkIShk3xiUDgZ1Ep8swJGu_i4hSCw",{"id":765,"title":766,"body":767,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":771,"navigation":100,"order":13,"path":772,"questions":773,"questionsCount":168,"related":169,"seo":838,"seoDescription":839,"stem":840,"subtopic":841,"topic":81,"topicSlug":83,"updated":420,"__hash__":842},"qa\u002Freact\u002Ftesting\u002Frtl-basics.md","Rtl Basics",{"type":91,"value":768,"toc":769},[],{"title":94,"searchDepth":11,"depth":11,"links":770},[],{},"\u002Freact\u002Ftesting\u002Frtl-basics",[774,778,782,786,790,794,798,802,806,810,814,818,822,826,830,834],{"id":775,"difficulty":96,"q":776,"a":777},"rtl-philosophy","What is React Testing Library and what is its core testing philosophy?","**React Testing Library (RTL)** is a lightweight testing utility built on\ntop of `@testing-library\u002Fdom`. Its core philosophy comes from a single\nguiding principle:\n\n> *\"The more your tests resemble the way your software is used, the more\n> confidence they can give you.\"* — Kent C. Dodds\n\nThis means RTL deliberately does **not** expose component internals\n(state, instance methods, refs). Instead, every query and assertion goes\nthrough the same DOM the user sees.\n\n```js\n\u002F\u002F ❌ Enzyme-style (tests implementation details)\nwrapper.state('isOpen')          \u002F\u002F couples test to internal state name\nwrapper.instance().handleClick() \u002F\u002F calls method directly, bypassing UI\n\n\u002F\u002F ✅ RTL-style (tests user-visible behavior)\nexpect(screen.queryByRole('dialog')).not.toBeInTheDocument()\nawait userEvent.click(screen.getByRole('button', { name: \u002Fopen\u002Fi }))\nexpect(screen.getByRole('dialog')).toBeInTheDocument()\n```\n\nThe practical consequences:\n- Queries find elements by **role, label, text, or test-id** — things real\n  users and assistive technology rely on.\n- Interactions go through `userEvent` or `fireEvent`, which dispatch real\n  DOM events rather than calling handlers directly.\n- If you can't test it without accessing internals, the API is probably\n  missing something worth exposing.\n\n**Rule of thumb:** If your test would still pass after a complete internal\nrefactor (renaming state, changing the component hierarchy), it's a good\nRTL test.\n",{"id":779,"difficulty":96,"q":780,"a":781},"render-api","What does `render()` do and what does it return?","`render()` mounts a React component into a detached `\u003Cdiv>` that is\nappended to `document.body`, runs all effects and state updates, then\nreturns a bag of query helpers bound to that container.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\n\nconst { getByText, queryByRole, findByLabelText, container, unmount, rerender } =\n  render(\u003CLoginForm \u002F>)\n\n\u002F\u002F Most common keys:\n\u002F\u002F container   — the raw DOM node wrapping the rendered output\n\u002F\u002F unmount()   — tear down the component (called automatically after each test)\n\u002F\u002F rerender()  — re-render with new props\n\u002F\u002F All getBy*\u002FqueryBy*\u002FfindBy* helpers (scoped to this render)\n```\n\nIn practice the bound helpers from the return value are rarely used\ndirectly. The **`screen`** object exports the same queries but always\noperates on `document.body`, which works even if multiple renders exist\nin one test.\n\n```js\nrender(\u003CLoginForm \u002F>)\n\u002F\u002F Prefer screen.* over the returned object\nconst button = screen.getByRole('button', { name: \u002Fsign in\u002Fi })\n```\n\nAfter each test, `@testing-library\u002Freact` calls `cleanup()` automatically\n(via an `afterEach` it registers), which unmounts the component and removes\nthe container from `document.body`.\n\n**Rule of thumb:** Always use `screen.*` for queries and keep the return\nvalue only when you need `rerender`, `unmount`, or `container`.\n",{"id":783,"difficulty":121,"q":784,"a":785},"query-variants","What are the three query variants — `getBy`, `queryBy`, `findBy` — and when should you use each?","RTL provides three prefixes for every query, each with different behavior\non missing elements and async awareness.\n\n| Variant | Not found | Multiple found | Async? |\n|---------|-----------|----------------|--------|\n| `getBy` | throws | throws | No |\n| `queryBy` | returns `null` | throws | No |\n| `findBy` | rejects (Promise) | rejects | Yes (retries until timeout) |\n\n```js\n\u002F\u002F getBy — use when element MUST exist right now\nconst heading = screen.getByRole('heading', { name: \u002Fdashboard\u002Fi })\n\n\u002F\u002F queryBy — use to assert element is ABSENT (toBeNull \u002F not.toBeInTheDocument)\nexpect(screen.queryByText('Error')).not.toBeInTheDocument()\n\n\u002F\u002F findBy — use when element appears AFTER an async operation\nconst item = await screen.findByText('Data loaded')  \u002F\u002F polls until found or timeout\n```\n\nAll three have `All` variants (`getAllBy`, `queryAllBy`, `findAllBy`) that\nreturn arrays and only throw when *no* elements are found (for `getAllBy`\nand `findAllBy`).\n\nCommon mistakes:\n- Using `getBy` to assert absence — it throws before you can check.\n- Forgetting `await` on `findBy` — returns a Promise, not the element.\n- Using `findBy` when the element is already there — wastes the polling\n  overhead.\n\n**Rule of thumb:** Get → exists now. Query → might not exist. Find → will\nexist soon.\n",{"id":787,"difficulty":96,"q":788,"a":789},"screen-object","What is `screen` and why is it preferred over the queries returned by `render()`?","`screen` is a re-export from `@testing-library\u002Fdom` that exposes the same\nfull query set but always targets `document.body` rather than a scoped\ncontainer.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\n\nrender(\u003CApp \u002F>)\n\n\u002F\u002F Both work, but screen.* is preferred\nconst { getByRole } = render(\u003CApp \u002F>)   \u002F\u002F ❌ scoped to this render's container\nscreen.getByRole('navigation')           \u002F\u002F ✅ searches all of document.body\n```\n\nAdvantages of `screen`:\n1. **No destructuring** — one import, always available.\n2. **Works across renders** — if a component portals into `document.body`\n   outside the container, `screen` still finds it.\n3. **`screen.debug()`** — prints the current DOM to the console for\n   quick inspection without needing the return value.\n\n```js\nscreen.debug()                     \u002F\u002F prints full document.body\nscreen.debug(screen.getByRole('dialog'))  \u002F\u002F prints just the dialog node\n```\n\n**Rule of thumb:** Always import and use `screen`; only keep the\n`render()` return value when you need `rerender`, `unmount`, or the raw\n`container` node.\n",{"id":791,"difficulty":121,"q":792,"a":793},"query-priority","In what priority order should you choose RTL queries, and why?","RTL's official priority list (from the docs) guides you toward queries\nthat reflect how real users and accessibility tools discover elements:\n\n1. **By role** (`getByRole`) — matches ARIA roles; works for buttons,\n   headings, links, inputs, dialogs, etc. Preferred for almost everything.\n2. **By label** (`getByLabelText`) — finds the form control associated\n   with a `\u003Clabel>`. Best for inputs, selects, textareas.\n3. **By placeholder** (`getByPlaceholderText`) — fallback when there is\n   no visible label.\n4. **By text** (`getByText`) — finds nodes by their text content.\n   Good for paragraphs, list items, non-interactive elements.\n5. **By display value** (`getByDisplayValue`) — current value of an\n   input\u002Fselect\u002Ftextarea.\n6. **By alt text** (`getByAltText`) — for images.\n7. **By title** (`getByTitle`) — `title` attribute.\n8. **By test id** (`getByTestId`) — only when nothing above is practical;\n   requires adding `data-testid` attributes to the DOM.\n\n```js\n\u002F\u002F ✅ Tier 1 — most accessible, most like how a user would find it\nscreen.getByRole('button', { name: \u002Fsubmit\u002Fi })\nscreen.getByRole('textbox', { name: \u002Femail\u002Fi })\n\n\u002F\u002F ✅ Tier 2 — great for forms with proper labels\nscreen.getByLabelText(\u002Fpassword\u002Fi)\n\n\u002F\u002F ⚠️ Last resort — opaque, requires markup change\nscreen.getByTestId('submit-btn')\n```\n\nThe deeper reason: queries that would break if you changed a class name\nor state field are brittle. Queries that break only when the visible UI\nchanges are meaningful.\n\n**Rule of thumb:** Reach for `getByRole` first; add `aria-label` or\n`aria-labelledby` to make otherwise unlabelled elements queryable before\nfalling back to `data-testid`.\n",{"id":795,"difficulty":121,"q":796,"a":797},"user-event-vs-fire-event","What is the difference between `userEvent` and `fireEvent`?","Both simulate user interactions, but at very different levels of fidelity.\n\n**`fireEvent`** dispatches a single synthetic DOM event and returns\nimmediately. It is synchronous and low-level.\n\n**`userEvent`** (from `@testing-library\u002Fuser-event`) simulates the *full\nsequence* of events a real user triggers: pointer down, pointer up, focus,\nkeydown, input, change, click, blur, etc. Since v14 it is async.\n\n```js\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { fireEvent, render, screen } from '@testing-library\u002Freact'\n\nrender(\u003CSearchBox \u002F>)\n\n\u002F\u002F fireEvent — fires one 'change' event, skips pointer\u002Fkeyboard events\nfireEvent.change(screen.getByRole('textbox'), { target: { value: 'react' } })\n\n\u002F\u002F userEvent — types character by character, fires keydown\u002Fkeypress\u002Finput\u002Fkeyup per char\nconst user = userEvent.setup()\nawait user.type(screen.getByRole('textbox'), 'react')\n```\n\nWhen to use each:\n- **userEvent** for anything the user actually does: typing, clicking,\n  selecting, hovering, tabbing. It is the right default.\n- **fireEvent** when you need to simulate an event that userEvent does not\n  support, or in unit tests where you want a specific event without the\n  full browser sequence.\n\nSetup pattern (v14+):\n```js\n\u002F\u002F Call setup once per test, not inside loops\nconst user = userEvent.setup()\nawait user.click(button)\nawait user.keyboard('{Enter}')\n```\n\n**Rule of thumb:** Default to `userEvent`; use `fireEvent` only when\n`userEvent` doesn't cover the event you need.\n",{"id":799,"difficulty":121,"q":800,"a":801},"waitfor","What is `waitFor` and when should you use it?","`waitFor` repeatedly executes a callback until it stops throwing (or the\ntimeout expires). It is used when an assertion depends on something that\nhasn't happened yet — a state update, an effect, or an async API call.\n\n```js\nimport { render, screen, waitFor } from '@testing-library\u002Freact'\n\ntest('shows success message after save', async () => {\n  render(\u003CSaveButton \u002F>)\n  await userEvent.setup().click(screen.getByRole('button', { name: \u002Fsave\u002Fi }))\n\n  \u002F\u002F The success message appears after a state update triggered by an API call\n  await waitFor(() => {\n    expect(screen.getByText('Saved!')).toBeInTheDocument()\n  })\n})\n```\n\nKey options:\n```js\nawait waitFor(callback, {\n  timeout: 3000,   \u002F\u002F default 1000 ms\n  interval: 50,    \u002F\u002F polling interval, default 50 ms\n})\n```\n\n`waitFor` vs `findBy*`:\n- `findBy*` is syntactic sugar for `waitFor(() => getBy*(...))` — use it\n  when you're waiting for a single element to appear.\n- `waitFor` is for more complex assertions (multiple elements, negative\n  assertions, or checking a value, not just presence).\n\nCommon mistake — putting multiple assertions in one `waitFor`:\n```js\n\u002F\u002F ❌ If the first passes then the second fails, waitFor retries the whole block\nawait waitFor(() => {\n  expect(a).toBeInTheDocument()\n  expect(b).toBeInTheDocument()  \u002F\u002F a keeps re-asserting, masking failures\n})\n\n\u002F\u002F ✅ Wait for the first, then assert synchronously\nawait screen.findByText('A')\nexpect(screen.getByText('B')).toBeInTheDocument()\n```\n\n**Rule of thumb:** Use `findBy*` for single async element appearances;\nuse `waitFor` for complex multi-assertion async scenarios.\n",{"id":803,"difficulty":96,"q":804,"a":805},"jest-dom-matchers","What does `@testing-library\u002Fjest-dom` provide and what are the most useful matchers?","`@testing-library\u002Fjest-dom` extends Jest\u002FVitest's `expect` with custom\nDOM matchers that produce clearer error messages than raw Jest assertions.\n\nSetup (once in your test setup file):\n```js\n\u002F\u002F vitest.config.ts \u002F jest.setup.ts\nimport '@testing-library\u002Fjest-dom'\n\u002F\u002F or\nimport '@testing-library\u002Fjest-dom\u002Fvitest'\n```\n\nMost useful matchers:\n\n```js\n\u002F\u002F Presence\nexpect(el).toBeInTheDocument()\nexpect(el).not.toBeInTheDocument()\n\n\u002F\u002F Visibility\nexpect(el).toBeVisible()           \u002F\u002F not hidden via CSS\nexpect(el).toBeEnabled()           \u002F\u002F not disabled\nexpect(el).toBeDisabled()\n\n\u002F\u002F Content\nexpect(el).toHaveTextContent('Hello')\nexpect(el).toHaveTextContent(\u002Fhello\u002Fi)\n\n\u002F\u002F Form state\nexpect(checkbox).toBeChecked()\nexpect(input).toHaveValue('react')\nexpect(select).toHaveDisplayValue('Option A')\n\n\u002F\u002F Attributes & classes\nexpect(el).toHaveAttribute('href', '\u002Fhome')\nexpect(el).toHaveClass('active')\n\n\u002F\u002F Focus\nexpect(input).toHaveFocus()\n```\n\nWithout jest-dom you'd write `expect(el).not.toBeNull()` instead of\n`expect(el).toBeInTheDocument()` — the latter produces a far more\ndescriptive failure message.\n\n**Rule of thumb:** Always import `@testing-library\u002Fjest-dom` in your\nglobal setup file so every test file gets the matchers automatically.\n",{"id":807,"difficulty":121,"q":808,"a":809},"within","What is `within()` and when is it useful?","`within()` scopes all queries to a specific DOM node, letting you select\nelements inside a particular region when multiple similar elements exist\non the page.\n\n```js\nimport { render, screen, within } from '@testing-library\u002Freact'\n\nrender(\n  \u003Cul>\n    \u003Cli>\n      \u003Cspan>Alice\u003C\u002Fspan>\n      \u003Cbutton>Delete\u003C\u002Fbutton>\n    \u003C\u002Fli>\n    \u003Cli>\n      \u003Cspan>Bob\u003C\u002Fspan>\n      \u003Cbutton>Delete\u003C\u002Fbutton>\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n)\n\n\u002F\u002F Without within — ambiguous: two \"Delete\" buttons exist\n\u002F\u002F screen.getByRole('button', { name: \u002Fdelete\u002Fi }) — throws (multiple found)\n\n\u002F\u002F With within — unambiguous\nconst aliceRow = screen.getByText('Alice').closest('li')\nwithin(aliceRow).getByRole('button', { name: \u002Fdelete\u002Fi }).click()\n```\n\nCommon use cases:\n- Tables with per-row actions\n- Lists where each item has the same repeated controls\n- Modals\u002Fdialogs — scope queries to just the dialog content\n\n```js\nconst dialog = screen.getByRole('dialog')\nexpect(within(dialog).getByRole('heading')).toHaveTextContent('Confirm')\nawait userEvent.setup().click(within(dialog).getByRole('button', { name: \u002Fcancel\u002Fi }))\n```\n\n**Rule of thumb:** When `getBy*` throws because multiple matching elements\nexist, use `within()` to narrow the search to the relevant container.\n",{"id":811,"difficulty":121,"q":812,"a":813},"async-testing-patterns","How do you test a component that fetches data on mount?","The standard pattern: render the component, assert the loading state,\nthen await the resolved state.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport { rest } from 'msw'        \u002F\u002F Mock Service Worker (preferred)\nimport { setupServer } from 'msw\u002Fnode'\n\nconst server = setupServer(\n  rest.get('\u002Fapi\u002Fusers', (req, res, ctx) =>\n    res(ctx.json([{ id: 1, name: 'Alice' }]))\n  )\n)\n\nbeforeAll(() => server.listen())\nafterEach(() => server.resetHandlers())\nafterAll(() => server.close())\n\ntest('shows users after fetch', async () => {\n  render(\u003CUserList \u002F>)\n\n  \u002F\u002F Assert loading state first (optional but good practice)\n  expect(screen.getByText(\u002Floading\u002Fi)).toBeInTheDocument()\n\n  \u002F\u002F Wait for data to appear\n  expect(await screen.findByText('Alice')).toBeInTheDocument()\n\n  \u002F\u002F Assert loading indicator is gone\n  expect(screen.queryByText(\u002Floading\u002Fi)).not.toBeInTheDocument()\n})\n\ntest('shows error on fetch failure', async () => {\n  server.use(\n    rest.get('\u002Fapi\u002Fusers', (req, res, ctx) => res(ctx.status(500)))\n  )\n  render(\u003CUserList \u002F>)\n  expect(await screen.findByText(\u002Fsomething went wrong\u002Fi)).toBeInTheDocument()\n})\n```\n\nWithout MSW you can mock `fetch` directly:\n```js\nglobal.fetch = vi.fn().mockResolvedValue({\n  ok: true,\n  json: async () => [{ id: 1, name: 'Alice' }],\n})\n```\n\n**Rule of thumb:** Prefer MSW for HTTP mocking — it intercepts at the\nnetwork level, so the same handlers work in both tests and the browser\nduring development.\n",{"id":815,"difficulty":96,"q":816,"a":817},"setup-vitest","How do you set up React Testing Library with Vitest?","Four steps:\n\n**1. Install dependencies**\n```bash\nnpm install -D vitest @vitest\u002Fui jsdom \\\n  @testing-library\u002Freact @testing-library\u002Fuser-event \\\n  @testing-library\u002Fjest-dom\n```\n\n**2. Configure Vitest (`vitest.config.ts`)**\n```ts\nimport { defineConfig } from 'vitest\u002Fconfig'\nimport react from '@vitejs\u002Fplugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n  test: {\n    environment: 'jsdom',          \u002F\u002F simulate the browser DOM\n    setupFiles: ['.\u002Fsrc\u002Ftest\u002Fsetup.ts'],\n  },\n})\n```\n\n**3. Create setup file (`src\u002Ftest\u002Fsetup.ts`)**\n```ts\nimport '@testing-library\u002Fjest-dom\u002Fvitest'   \u002F\u002F custom matchers\nimport { cleanup } from '@testing-library\u002Freact'\nimport { afterEach } from 'vitest'\n\nafterEach(() => cleanup())  \u002F\u002F auto-cleanup (RTL registers this automatically, but explicit is safer)\n```\n\n**4. Write a test**\n```ts\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { describe, it, expect } from 'vitest'\nimport Counter from '.\u002FCounter'\n\ndescribe('Counter', () => {\n  it('increments on click', async () => {\n    render(\u003CCounter \u002F>)\n    await userEvent.setup().click(screen.getByRole('button', { name: \u002Fincrement\u002Fi }))\n    expect(screen.getByText('1')).toBeInTheDocument()\n  })\n})\n```\n\n**Rule of thumb:** Set `environment: 'jsdom'` and import `jest-dom` in\nyour setup file — those two steps cover 90% of RTL configuration.\n",{"id":819,"difficulty":121,"q":820,"a":821},"rerender","How do you test a component that should update when its props change?","Use the `rerender` function returned by `render()`. It re-renders the same\ncomponent with new props without unmounting\u002Fremounting.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\n\nfunction Greeting({ name }) {\n  return \u003Ch1>Hello, {name}!\u003C\u002Fh1>\n}\n\ntest('updates when name prop changes', () => {\n  const { rerender } = render(\u003CGreeting name=\"Alice\" \u002F>)\n  expect(screen.getByRole('heading')).toHaveTextContent('Hello, Alice!')\n\n  rerender(\u003CGreeting name=\"Bob\" \u002F>)\n  expect(screen.getByRole('heading')).toHaveTextContent('Hello, Bob!')\n})\n```\n\n`rerender` is also useful for testing prop-driven animations or conditional\nrendering toggled from outside:\n\n```js\ntest('hides content when visible prop is false', () => {\n  const { rerender } = render(\u003CDrawer visible={true}>\u003Cp>Content\u003C\u002Fp>\u003C\u002FDrawer>)\n  expect(screen.getByText('Content')).toBeVisible()\n\n  rerender(\u003CDrawer visible={false}>\u003Cp>Content\u003C\u002Fp>\u003C\u002FDrawer>)\n  expect(screen.queryByText('Content')).not.toBeInTheDocument()\n})\n```\n\n**Rule of thumb:** Use `rerender` to test prop changes; use `userEvent`\nto test changes driven by user interaction.\n",{"id":823,"difficulty":121,"q":824,"a":825},"wrapping-providers","How do you render a component that requires context providers or a router?","Pass a `wrapper` option to `render()` that wraps the component under test\nwith any necessary providers.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport { MemoryRouter } from 'react-router-dom'\nimport { ThemeProvider } from '.\u002FThemeContext'\n\n\u002F\u002F Inline wrapper\nrender(\u003CNavBar \u002F>, {\n  wrapper: ({ children }) => (\n    \u003CMemoryRouter initialEntries={['\u002Fdashboard']}>\n      \u003CThemeProvider theme=\"dark\">\n        {children}\n      \u003C\u002FThemeProvider>\n    \u003C\u002FMemoryRouter>\n  ),\n})\n```\n\nFor reuse across many tests, create a custom `render` helper:\n\n```js\n\u002F\u002F test\u002Futils.tsx\nimport { render } from '@testing-library\u002Freact'\nimport { MemoryRouter } from 'react-router-dom'\nimport { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query'\n\nfunction AllProviders({ children }) {\n  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } })\n  return (\n    \u003CQueryClientProvider client={client}>\n      \u003CMemoryRouter>{children}\u003C\u002FMemoryRouter>\n    \u003C\u002FQueryClientProvider>\n  )\n}\n\nconst customRender = (ui, options) =>\n  render(ui, { wrapper: AllProviders, ...options })\n\nexport * from '@testing-library\u002Freact'  \u002F\u002F re-export everything\nexport { customRender as render }        \u002F\u002F override render\n```\n\nThen import from `test\u002Futils` instead of `@testing-library\u002Freact` in\nevery test file.\n\n**Rule of thumb:** Create a single `customRender` helper with all app-wide\nproviders so tests don't repeat provider boilerplate.\n",{"id":827,"difficulty":96,"q":828,"a":829},"debug-failing-tests","What tools does RTL provide to debug failing tests?","RTL offers several built-in debugging tools:\n\n**`screen.debug()`** — prints the current DOM state to the console:\n```js\nrender(\u003CComplexForm \u002F>)\nscreen.debug()                          \u002F\u002F prints all of document.body\nscreen.debug(screen.getByRole('form'))  \u002F\u002F prints just that subtree\n```\n\n**`screen.logTestingPlaygroundURL()`** — generates a URL to the\nTesting Playground website pre-loaded with the current DOM, so you can\ninteractively explore which query to use:\n```js\nscreen.logTestingPlaygroundURL()\n\u002F\u002F Logs: https:\u002F\u002Ftesting-playground.com\u002F#markup=...\n```\n\n**`prettyDOM()`** — programmatic version of debug, returns a string:\n```js\nimport { prettyDOM } from '@testing-library\u002Freact'\nconsole.log(prettyDOM(document.body))\nconsole.log(prettyDOM(someElement, 5000))  \u002F\u002F second arg: max length\n```\n\n**Query suggestion in error messages** — when a query fails, RTL v13+\nsuggests alternative queries that would have matched:\n```\nTestingLibraryElementError: Unable to find an accessible element with the\nrole \"button\" and name \"Submit\"\n\nHere are the accessible roles:\n  button:\n    Name \"submit\":\n    \u003Cbutton>submit\u003C\u002Fbutton>  ← hint: case-sensitive name option used\n```\n\n**Rule of thumb:** When a test fails with \"Unable to find element,\" call\n`screen.debug()` immediately to see the actual DOM at that point.\n",{"id":831,"difficulty":208,"q":832,"a":833},"custom-queries","Can you create custom RTL queries, and when would you need one?","Yes. Custom queries follow the same `getBy`\u002F`queryBy`\u002F`findBy` contract\nand are useful when none of the built-in queries fit your domain — for\nexample, querying by a data attribute specific to your component library.\n\n```js\nimport { buildQueries, queryHelpers } from '@testing-library\u002Freact'\n\n\u002F\u002F Step 1 — the raw queryAll function\nconst queryAllByDataCy = (container, id) =>\n  queryHelpers.queryAllByAttribute('data-cy', container, id)\n\n\u002F\u002F Step 2 — build the full query family from queryAll\nconst [\n  queryByDataCy,\n  getAllByDataCy,\n  getByDataCy,\n  findAllByDataCy,\n  findByDataCy,\n] = buildQueries(\n  queryAllByDataCy,\n  (c, id) => `Found multiple elements with data-cy=\"${id}\"`,\n  (c, id) => `Unable to find element with data-cy=\"${id}\"`\n)\n\nexport { queryByDataCy, getAllByDataCy, getByDataCy, findAllByDataCy, findByDataCy }\n```\n\nWire them into a custom render:\n```js\nconst customRender = (ui, options) =>\n  render(ui, {\n    queries: { ...queries, getByDataCy, findByDataCy },\n    ...options,\n  })\n```\n\nIn practice, the need for custom queries is rare. Common triggers:\n- Your component library uses a non-standard attribute as the accessible\n  identifier and you can't change it.\n- You need to query by a complex compound condition.\n\n**Rule of thumb:** Exhaust all built-in queries (especially `getByRole`\nwith `name` and `within`) before writing custom ones — they're extra\nmaintenance overhead.\n",{"id":835,"difficulty":121,"q":836,"a":837},"render-hook","When should you use `renderHook` instead of rendering a component?","`renderHook` is for testing **custom hooks in isolation** — when the hook\nlogic is complex enough to warrant direct tests but you don't want to\ncreate a throwaway wrapper component manually.\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { useCounter } from '.\u002FuseCounter'\n\ntest('increments the counter', () => {\n  const { result } = renderHook(() => useCounter(0))\n\n  \u002F\u002F result.current holds the hook's return value\n  expect(result.current.count).toBe(0)\n\n  act(() => {\n    result.current.increment()\n  })\n\n  expect(result.current.count).toBe(1)\n})\n```\n\nIt also accepts a `wrapper` for context:\n```js\nconst { result } = renderHook(() => useAuth(), {\n  wrapper: ({ children }) => \u003CAuthProvider>{children}\u003C\u002FAuthProvider>,\n})\n```\n\nWhen **not** to use `renderHook`:\n- When the hook is trivial and already covered by component tests.\n- When you'd rather test the hook through the component that uses it\n  (integration test) — usually the better approach.\n\n**Rule of thumb:** Use `renderHook` when a custom hook encapsulates\nmeaningful logic (state machines, async flows, caching) that deserves\ndirect tests beyond what component-level tests catch.\n",{"description":94},"React Testing Library interview questions — render, screen, queries, userEvent, waitFor, jest-dom matchers, async testing, and the RTL testing philosophy.","react\u002Ftesting\u002Frtl-basics","RTL Basics","FQwmpxfpp_kNFH3s3_yIPSe9cgpVhuzelX5XtQbSoOE",{"id":844,"title":845,"body":846,"description":94,"difficulty":96,"extension":97,"framework":10,"frameworkSlug":8,"meta":850,"navigation":100,"order":11,"path":852,"questions":853,"questionsCount":168,"related":169,"seo":918,"seoDescription":919,"stem":920,"subtopic":921,"topic":28,"topicSlug":29,"updated":174,"__hash__":922},"qa\u002Freact\u002Fcomponents\u002Fprops-component-types.md","Props Component Types",{"type":91,"value":847,"toc":848},[],{"title":94,"searchDepth":11,"depth":11,"links":849},[],{"subtopicSlug":851},"props-component-types","\u002Freact\u002Fcomponents\u002Fprops-component-types",[854,858,862,866,870,874,878,882,886,890,894,898,902,906,910,914],{"id":855,"difficulty":96,"q":856,"a":857},"what-are-props","What are props in React?","**Props** (short for properties) are the mechanism React uses to pass data\n**down** from a parent component to a child. They are received by the child\nas a plain JavaScript object and are **read-only** — a component must never\nmodify its own props.\n\n```jsx\n\u002F\u002F Parent passes props\n\u003CGreeting name=\"Alice\" age={30} isAdmin \u002F>\n\n\u002F\u002F Child receives them\nfunction Greeting({ name, age, isAdmin }) {\n  return \u003Cp>{name} ({age}) — {isAdmin ? 'Admin' : 'User'}\u003C\u002Fp>\n}\n```\n\nProps can be any JavaScript value: strings, numbers, booleans, objects,\narrays, or even functions and React elements. Whatever you put between\nopening and closing tags flows in as `children`.\n\n**Rule of thumb:** props flow down, never up. If a child needs to\ncommunicate to its parent, pass a callback function as a prop.\n",{"id":859,"difficulty":96,"q":860,"a":861},"props-immutable","Are props mutable inside a component? Why not?","No. Props are **read-only** in React by design. A component is a pure\nfunction of its props (and state): given the same inputs it should always\nproduce the same output.\n\n```jsx\nfunction Badge({ count }) {\n  \u002F\u002F ❌ Never do this\n  count = count + 1\n\n  \u002F\u002F ✅ Derive a new value without mutating\n  const displayCount = count > 99 ? '99+' : count\n  return \u003Cspan>{displayCount}\u003C\u002Fspan>\n}\n```\n\nIf you try to reassign a prop it may work locally (it's just a JS variable),\nbut the change won't persist across renders and will confuse React's\nreconciler because the parent still owns the original value.\n\n**Rule of thumb:** treat every prop as a `const`. To derive a modified\nvalue, create a new local variable; to change what the parent passes, fire\na callback that updates the parent's state.\n",{"id":863,"difficulty":96,"q":864,"a":865},"children-prop","What is the children prop?","`children` is the implicit prop that React populates with whatever you put\n**between** a component's JSX tags. It enables composition — parent decides\nthe shell, child slot receives arbitrary content.\n\n```jsx\nfunction Panel({ title, children }) {\n  return (\n    \u003Csection className=\"panel\">\n      \u003Ch2>{title}\u003C\u002Fh2>\n      \u003Cdiv className=\"panel-body\">{children}\u003C\u002Fdiv>\n    \u003C\u002Fsection>\n  )\n}\n\n\u002F\u002F Usage\n\u003CPanel title=\"Stats\">\n  \u003Cp>Revenue: $1,200\u003C\u002Fp>\n  \u003CChart data={data} \u002F>\n\u003C\u002FPanel>\n```\n\n`children` can be a string, a single element, an array of elements, a\nfunction (render prop), or `undefined` (nothing passed). Use\n`React.Children.count(children)` or `React.Children.toArray(children)` to\ninspect it programmatically.\n\n**Rule of thumb:** `children` is just a prop with special JSX sugar. It\nlets you build generic \"container\" components that don't need to know what\ngoes inside them.\n",{"id":867,"difficulty":121,"q":868,"a":869},"function-vs-class","What is the difference between a function component and a class component?","| | Function component | Class component |\n|---|---|---|\n| Syntax | Plain JS function | `class X extends React.Component` |\n| State | `useState`, `useReducer` | `this.state` \u002F `this.setState` |\n| Lifecycle | `useEffect` | `componentDidMount`, `componentDidUpdate`, etc. |\n| Context | `useContext` | `static contextType` \u002F `Context.Consumer` |\n| `this` | Not applicable | Required for everything |\n| Error boundaries | Not supported | `componentDidCatch` |\n\n```jsx\n\u002F\u002F Function component\nfunction Counter() {\n  const [n, setN] = useState(0)\n  return \u003Cbutton onClick={() => setN(n + 1)}>{n}\u003C\u002Fbutton>\n}\n\n\u002F\u002F Class component — equivalent\nclass Counter extends React.Component {\n  state = { n: 0 }\n  render() {\n    return \u003Cbutton onClick={() => this.setState({ n: this.state.n + 1 })}>{this.state.n}\u003C\u002Fbutton>\n  }\n}\n```\n\nSince React 16.8 (hooks), function components can do everything class\ncomponents can except implement error boundaries. New code should use\nfunction components.\n\n**Rule of thumb:** always write function components. Only reach for a class\nwhen you need a `componentDidCatch` error boundary — or when you're\nmaintaining legacy code.\n",{"id":871,"difficulty":121,"q":872,"a":873},"when-class-component","Is there any reason to use a class component in modern React?","The only built-in feature class components have that function components\nlack is the ability to serve as an **error boundary** via\n`componentDidCatch` and `getDerivedStateFromError`. There is no hooks-based\nequivalent in React itself (as of 2024).\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false }\n\n  static getDerivedStateFromError() {\n    return { hasError: true }\n  }\n\n  componentDidCatch(err, info) {\n    logError(err, info)\n  }\n\n  render() {\n    return this.state.hasError\n      ? \u003Cp>Something went wrong.\u003C\u002Fp>\n      : this.props.children\n  }\n}\n```\n\nIn practice most teams write a single `ErrorBoundary` class component and\nreach for third-party wrappers like `react-error-boundary` everywhere else.\n\n**Rule of thumb:** one class component per project (the error boundary).\nEverything else is a function component.\n",{"id":875,"difficulty":96,"q":876,"a":877},"default-props","How do you provide default values for props?","The modern idiomatic approach is **default parameter destructuring**:\n\n```jsx\nfunction Button({ label = 'Click me', variant = 'primary', disabled = false }) {\n  return \u003Cbutton className={variant} disabled={disabled}>{label}\u003C\u002Fbutton>\n}\n```\n\nAlternatively, you can use the `defaultProps` static property, but this is\nconsidered legacy and may be removed in a future major version:\n\n```jsx\nButton.defaultProps = {\n  label: 'Click me',\n  variant: 'primary',\n  disabled: false,\n}\n```\n\nFor TypeScript users, the destructuring approach integrates cleanly with\ntype annotations:\n\n```tsx\ntype ButtonProps = { label?: string; variant?: 'primary' | 'secondary' }\nfunction Button({ label = 'Click me', variant = 'primary' }: ButtonProps) { … }\n```\n\n**Rule of thumb:** use destructuring defaults; they're co-located with the\nparameter list and work without any React-specific API.\n",{"id":879,"difficulty":121,"q":880,"a":881},"proptypes","What is PropTypes and when would you use it?","`PropTypes` is a runtime prop validation library that ships separately\n(`prop-types` package). In development mode it logs warnings when props\nfail the declared type, catching mistakes early.\n\n```jsx\nimport PropTypes from 'prop-types'\n\nfunction Avatar({ name, size, src }) {\n  return \u003Cimg src={src} alt={name} width={size} \u002F>\n}\n\nAvatar.propTypes = {\n  name: PropTypes.string.isRequired,\n  size: PropTypes.number,\n  src: PropTypes.string.isRequired,\n}\n\nAvatar.defaultProps = { size: 48 }\n```\n\nPropTypes are stripped in production builds. The trade-off vs TypeScript:\n- **PropTypes** — runtime, no build-step, catches type mistakes at render\n  time in any JS project.\n- **TypeScript** — compile-time, catches mistakes before runtime, broader\n  coverage (not just props).\n\n**Rule of thumb:** in a TypeScript project, TypeScript alone is sufficient.\nIn a plain JS project, PropTypes are a lightweight safety net.\n",{"id":883,"difficulty":96,"q":884,"a":885},"props-vs-state","What is the difference between props and state?","| | Props | State |\n|---|---|---|\n| Owned by | Parent component | Component itself |\n| Mutable by | Parent only | The component (`setState` \u002F setter) |\n| Triggers re-render | When parent re-renders | When updated via setter |\n| Purpose | Pass data in | Track internal data that changes |\n\n```jsx\nfunction Thermometer({ unit }) {       \u002F\u002F unit is a prop — parent decides\n  const [temp, setTemp] = useState(20) \u002F\u002F temp is state — component owns it\n  return (\n    \u003Cdiv>\n      {temp}° {unit}\n      \u003Cbutton onClick={() => setTemp(t => t + 1)}>+\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\nA useful mental model: props are like function arguments; state is like\nlocal variables that persist between calls.\n\n**Rule of thumb:** if a value is determined by the outside world, it's a\nprop. If it's determined by the component itself and can change, it's state.\n",{"id":887,"difficulty":121,"q":888,"a":889},"prop-drilling","What is prop drilling and what are the alternatives?","**Prop drilling** occurs when a prop must be passed through several\nintermediate layers that don't use it — just to get it to a deeply nested\ncomponent.\n\n```jsx\n\u002F\u002F Drilling `user` through three layers that don't care about it\n\u003CApp user={user}>\n  \u003CLayout user={user}>\n    \u003CSidebar user={user}>\n      \u003CAvatar user={user} \u002F>   {\u002F* ← only Avatar actually uses it *\u002F}\n    \u003C\u002FSidebar>\n  \u003C\u002FLayout>\n\u003C\u002FApp>\n```\n\nAlternatives:\n1. **Context API** — `React.createContext` + `useContext`. Best for\n   genuinely global or cross-cutting data (theme, current user, locale).\n2. **Component composition \u002F children** — pass `\u003CAvatar user={user} \u002F>`\n   as a child instead of passing `user` through intermediaries.\n3. **State management library** — Zustand, Redux, Jotai — good when many\n   unrelated components need the same data.\n\n**Rule of thumb:** before reaching for Context, try restructuring with\ncomposition. Context adds implicit coupling; explicit props are easier to\ntrace.\n",{"id":891,"difficulty":96,"q":892,"a":893},"destructuring-props","How do you destructure props and why is it preferred?","Destructure props directly in the function signature rather than accessing\nthem via the `props` object — it's more concise and makes required fields\nobvious.\n\n```jsx\n\u002F\u002F Verbose\nfunction Card(props) {\n  return \u003Cdiv className={props.variant}>{props.children}\u003C\u002Fdiv>\n}\n\n\u002F\u002F Destructured — preferred\nfunction Card({ variant, children }) {\n  return \u003Cdiv className={variant}>{children}\u003C\u002Fdiv>\n}\n\n\u002F\u002F With defaults and rest\nfunction Card({ variant = 'default', className = '', children, ...rest }) {\n  return (\n    \u003Cdiv className={`card card-${variant} ${className}`} {...rest}>\n      {children}\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n**Rule of thumb:** destructure all props at the function signature. For\nlarge prop objects, consider grouping related props into sub-objects\n(`position={{ x, y }}`) to keep the signature readable.\n",{"id":895,"difficulty":96,"q":896,"a":897},"handler-as-prop","How do you pass an event handler as a prop?","Pass the function **reference** as a prop, not the function call. The child\nreceives it and calls it when the event fires.\n\n```jsx\n\u002F\u002F Parent\nfunction Form() {\n  function handleSubmit(data) {\n    console.log(data)\n  }\n  return \u003CSubmitButton onSubmit={handleSubmit} label=\"Save\" \u002F>\n}\n\n\u002F\u002F Child — receives and calls the handler\nfunction SubmitButton({ onSubmit, label }) {\n  return \u003Cbutton onClick={() => onSubmit({ time: Date.now() })}>{label}\u003C\u002Fbutton>\n}\n```\n\nConvention: name callback props with the `on` prefix (`onChange`, `onClose`,\n`onSubmit`) to signal that they are event-like.\n\n**Rule of thumb:** pass function references, not calls. `onClick={handle}`\npasses the function; `onClick={handle()}` calls it immediately and passes\nits return value — almost never what you want.\n",{"id":899,"difficulty":121,"q":900,"a":901},"spread-props","What is the spread-props pattern and when should you use it?","Spread props (`{...rest}`) forward a batch of unknown props to a child\nwithout enumerating each one. It's most useful in wrapper\u002Fadapter\ncomponents.\n\n```jsx\nfunction Input({ label, error, ...inputProps }) {\n  \u002F\u002F label and error are consumed here; everything else goes to \u003Cinput>\n  return (\n    \u003Cdiv>\n      \u003Clabel>{label}\u003C\u002Flabel>\n      \u003Cinput {...inputProps} \u002F>\n      {error && \u003Cspan className=\"error\">{error}\u003C\u002Fspan>}\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage — all native \u003Cinput> attributes work without any boilerplate\n\u003CInput label=\"Email\" type=\"email\" name=\"email\" required error={errors.email} \u002F>\n```\n\n**Caution:** spreading onto DOM elements can forward non-standard props,\nwhich React will warn about. Always destructure known props first and spread\nthe remainder.\n\n**Rule of thumb:** spread `...rest` to DOM wrappers; avoid spreading the\nfull props object without filtering — it leaks internal props.\n",{"id":903,"difficulty":121,"q":904,"a":905},"pure-component","What is a pure component in React?","A **pure component** is one whose output depends only on its props (and\nstate), with no side effects during rendering. Given the same inputs it\nalways returns the same JSX — like a pure function in FP.\n\nIn class components, `React.PureComponent` adds a shallow-equality check\nin `shouldComponentUpdate`, preventing re-renders when props haven't\nchanged.\n\nIn function components, `React.memo` provides the same shallow-equality\nbail-out:\n\n```jsx\n\u002F\u002F Without memo — re-renders every time parent re-renders\nfunction Label({ text }) {\n  return \u003Cspan>{text}\u003C\u002Fspan>\n}\n\n\u002F\u002F With memo — skips re-render if text hasn't changed\nconst Label = React.memo(function Label({ text }) {\n  return \u003Cspan>{text}\u003C\u002Fspan>\n})\n```\n\n**Rule of thumb:** apply `React.memo` only to components that render\nfrequently with the same props and are measurably expensive. Premature\nmemoization adds complexity without benefit — profile first.\n",{"id":907,"difficulty":121,"q":908,"a":909},"composition-vs-inheritance","Why does React prefer composition over inheritance?","React's component model is built around **composition**: assembling behavior\nby combining components, not by extending them. The React team explicitly\nrecommends against using inheritance between components (other than extending\n`React.Component` once).\n\nComposition approaches:\n- **Children prop** — slot any content into a component.\n- **Specialized components** — make a `\u003CDialog>` and then a `\u003CAlertDialog>`\n  that renders a pre-configured `\u003CDialog>`, not one that extends it.\n- **Render props \u002F HOCs** — share logic without shared class hierarchies.\n\n```jsx\n\u002F\u002F ✅ Composition\nfunction SuccessDialog({ children }) {\n  return \u003CDialog icon=\"check\" title=\"Success\">{children}\u003C\u002FDialog>\n}\n\n\u002F\u002F ❌ Inheritance — unusual in React, actively discouraged\nclass SuccessDialog extends Dialog { … }\n```\n\n**Rule of thumb:** when you think \"I want a component like X but with Y\nchanged,\" reach for composition (wrap, extend via props, or use children)\nbefore ever reaching for class inheritance.\n",{"id":911,"difficulty":121,"q":912,"a":913},"controlled-component","What is a controlled component?","A **controlled component** is a form element whose value is **driven by\nReact state** rather than by the DOM. React is the single source of truth.\n\n```jsx\nfunction LoginForm() {\n  const [email, setEmail] = useState('')\n\n  return (\n    \u003Cinput\n      type=\"email\"\n      value={email}          \u002F\u002F state drives the displayed value\n      onChange={e => setEmail(e.target.value)}   \u002F\u002F state updated on change\n    \u002F>\n  )\n}\n```\n\nEvery keystroke calls `setEmail`, which updates state, which re-renders,\nwhich sets `value` — a tight loop. This means you can validate, transform,\nor intercept input on every change.\n\nThe alternative, **uncontrolled components**, read values from the DOM via\na ref at submission time rather than tracking each change.\n\n**Rule of thumb:** use controlled inputs when you need real-time validation\nor to format input as the user types; uncontrolled when the form is simple\nand you only need values on submit.\n",{"id":915,"difficulty":208,"q":916,"a":917},"render-props","What is the render-props pattern?","A **render prop** is a prop whose value is a function, allowing one\ncomponent to delegate rendering decisions to its parent. It's a technique\nfor sharing stateful logic without hooks or HOCs.\n\n```jsx\n\u002F\u002F Provider of the logic\nfunction MouseTracker({ render }) {\n  const [pos, setPos] = useState({ x: 0, y: 0 })\n  return (\n    \u003Cdiv onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>\n      {render(pos)}   {\u002F* call the function to get JSX *\u002F}\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Consumer decides what to display\n\u003CMouseTracker render={({ x, y }) => (\n  \u003Cp>Mouse at {x}, {y}\u003C\u002Fp>\n)} \u002F>\n```\n\nThe `children` prop can be used the same way (and often is):\n\n```jsx\n\u003CMouseTracker>\n  {({ x, y }) => \u003Cp>Mouse at {x}, {y}\u003C\u002Fp>}\n\u003C\u002FMouseTracker>\n```\n\nIn modern code, custom hooks replace most render-prop use cases with less\nindirection.\n\n**Rule of thumb:** know the pattern for interviews; in practice, custom\nhooks are almost always cleaner.\n",{"description":94},"React props and component types interview questions — function vs class components, children, defaultProps, PropTypes, composition over inheritance, and controlled components.","react\u002Fcomponents\u002Fprops-component-types","Props and Component Types","JOU2b0dj3z8YqBLCQ001LOO430R9KMVhifMegiHzHE8",{"id":924,"title":925,"body":926,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":930,"navigation":100,"order":11,"path":931,"questions":932,"questionsCount":1073,"related":169,"seo":1074,"seoDescription":1075,"stem":1076,"subtopic":1077,"topic":20,"topicSlug":21,"updated":328,"__hash__":1078},"qa\u002Freact\u002Fhooks\u002Fuseeffect.md","Useeffect",{"type":91,"value":927,"toc":928},[],{"title":94,"searchDepth":11,"depth":11,"links":929},[],{},"\u002Freact\u002Fhooks\u002Fuseeffect",[933,937,941,945,949,953,957,961,965,969,973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053,1057,1061,1065,1069],{"id":934,"difficulty":96,"q":935,"a":936},"what-is-useeffect","What does useEffect do?","`useEffect` lets you run **side effects** — work that reaches outside React's\nrender output: fetching data, setting up subscriptions or timers, manually\ntouching the DOM, logging. Render must stay pure (no side effects), so React\ngives you `useEffect` as the escape hatch that runs **after** the component has\nrendered and the screen is updated.\n\n```jsx\nuseEffect(() => {\n  document.title = `${count} unread`\n}, [count])\n```\n\nThe mental model isn't \"run this on mount\u002Fupdate\" but \"**keep this external\nthing in sync** with the state and props in my dependency array.\" React runs\nthe effect whenever one of those inputs changes so the outside world matches\nthe latest render.\n",{"id":938,"difficulty":121,"q":939,"a":940},"deps-array","What does the dependency array control?","The dependency array tells React **when to re-run** the effect by comparing\neach item to its value from the previous render (using `Object.is`):\n\n- `[]` -> run **once** after the first render (no dependencies ever change).\n- `[a, b]` -> run after the first render, then again **only when `a` or `b`\n  change**.\n- **omitted** -> run after **every** render.\n\n```jsx\nuseEffect(() => {\n  const sub = source.subscribe(id)\n  return () => sub.unsubscribe()\n}, [id]) \u002F\u002F re-subscribe only when `id` changes\n```\n\nThe golden rule (enforced by the `react-hooks\u002Fexhaustive-deps` lint): every\nreactive value the effect *reads* — props, state, derived values — must be\nlisted. Omitting one to \"run less often\" is the #1 source of stale-data bugs.\n",{"id":942,"difficulty":121,"q":943,"a":944},"cleanup","What is the cleanup function and when does it run?","If your effect sets something up that needs tearing down, **return a function**\nfrom it — that's the cleanup. React runs it in two situations: **before\nre-running** the effect (to undo the previous run) and when the component\n**unmounts**. This prevents leaked listeners, duplicate subscriptions, and\n\"setState on unmounted component\" warnings.\n\n```jsx\nuseEffect(() => {\n  const id = setInterval(tick, 1000)\n  return () => clearInterval(id) \u002F\u002F tear down before next run \u002F on unmount\n}, [])\n```\n\nThe sequence on a dependency change is: run cleanup for the *old* deps -> run\nthe effect for the *new* deps. So with `[id]`, changing `id` from `1` to `2`\nunsubscribes from `1` first, then subscribes to `2`. Forgetting cleanup is how\nyou end up with N intervals firing after N renders.\n",{"id":946,"difficulty":96,"q":947,"a":948},"empty-deps","What happens with an empty dependency array?","An empty array `[]` means \"no reactive dependencies,\" so the effect runs\n**once** after the initial render and its cleanup runs **once** on unmount.\nIt's the closest hooks equivalent to the old `componentDidMount` +\n`componentWillUnmount` lifecycle pair.\n\n```jsx\nuseEffect(() => {\n  const onResize = () => setWidth(window.innerWidth)\n  window.addEventListener('resize', onResize)\n  return () => window.removeEventListener('resize', onResize)\n}, []) \u002F\u002F attach once, detach on unmount\n```\n\nCaveat for interviews: in React 18 **Strict Mode during development**, React\nintentionally mounts -> unmounts -> remounts components, so your `[]` effect and\nits cleanup fire twice. That's a deliberate check that your cleanup is correct;\nit does not happen in production.\n",{"id":950,"difficulty":208,"q":951,"a":952},"stale-closure","What is a stale closure in useEffect?","An effect closes over the props and state from the render it was created in.\nIf you read a value but **leave it out of the dependency array**, the effect\nkeeps using the *frozen* value from that render and never sees updates — a\nstale closure.\n\n```jsx\n\u002F\u002F count is captured once (as 0) and never updated -> logs 0, 0, 0...\nuseEffect(() => {\n  const id = setInterval(() => console.log(count), 1000)\n  return () => clearInterval(id)\n}, []) \u002F\u002F missing `count`\n\n\u002F\u002F Option A: list the dependency (re-creates the interval each change)\n\u002F\u002F Option B: use a functional updater so you don't read `count` at all\nsetCount(c => c + 1)\n```\n\nFixes: include every value you read in the deps, use the functional updater\nform so the stale value never matters, or stash the latest value in a `useRef`\nwhen you deliberately want a long-lived effect that reads fresh data.\n",{"id":954,"difficulty":208,"q":955,"a":956},"effect-timing","How does useEffect differ from useLayoutEffect?","Both run after render, but at different moments relative to the browser\n**paint**:\n\n- `useEffect` runs **asynchronously, after the browser has painted**. The user\n  sees the new frame, then the effect fires. Best for the vast majority of\n  effects (data, subscriptions) since it doesn't block visual updates.\n- `useLayoutEffect` runs **synchronously after DOM mutations but before\n  paint**. React blocks painting until it finishes, so you can measure layout\n  or mutate the DOM and the user never sees an intermediate state.\n\n```jsx\nuseLayoutEffect(() => {\n  const { height } = ref.current.getBoundingClientRect()\n  setTooltipTop(height) \u002F\u002F adjust position before the browser paints\n}, [])\n```\n\nUse `useLayoutEffect` only when you must read\u002Fwrite layout to avoid a visible\nflicker; because it's blocking, overusing it hurts performance. (On the server\nit doesn't run and warns — guard SSR code accordingly.)\n",{"id":958,"difficulty":121,"q":959,"a":960},"data-fetching","How do you fetch data inside useEffect?","Start the request in the effect and store the result in state. List every value\nthe request depends on (like an `id`) in the dependency array so it refetches\nwhen they change.\n\n```jsx\nuseEffect(() => {\n  let active = true\n  fetch(`\u002Fapi\u002Fuser\u002F${id}`)\n    .then(r => r.json())\n    .then(data => { if (active) setUser(data) })\n  return () => { active = false } \u002F\u002F ignore a stale response\n}, [id])\n```\n\nThe `active` flag (or an `AbortController`) prevents a slow earlier request from\noverwriting a newer one. In real apps a data library usually handles this better.\n",{"id":962,"difficulty":208,"q":963,"a":964},"fetch-race-condition","How do you avoid race conditions when fetching in an effect?","If `id` changes quickly, responses can arrive **out of order** and the older one\noverwrites the newer. Guard with a cleanup that invalidates the in-flight\nrequest — an ignore flag or an `AbortController`.\n\n```jsx\nuseEffect(() => {\n  const ctrl = new AbortController()\n  fetch(`\u002Fapi\u002F${id}`, { signal: ctrl.signal })\n    .then(r => r.json())\n    .then(setData)\n    .catch(e => { if (e.name !== 'AbortError') throw e })\n  return () => ctrl.abort() \u002F\u002F cancel the previous request\n}, [id])\n```\n\nReact runs the cleanup before the next effect, so the stale request is aborted\nand can't clobber fresh data.\n",{"id":966,"difficulty":121,"q":967,"a":968},"fetch-vs-library","Why prefer a data library over fetching in useEffect?","Hand-rolled `useEffect` fetching means you reimplement caching, deduping,\nretries, race-condition handling, loading\u002Ferror states, and refetching — for\nevery call. Libraries like **React Query \u002F SWR \u002F RTK Query** give you all of that\ndeclaratively.\n\n```jsx\nconst { data, isLoading, error } = useQuery({\n  queryKey: ['user', id],\n  queryFn: () => fetchUser(id),\n})\n```\n\nThe React docs explicitly recommend a framework or data library for fetching.\nUse raw effect-fetching only for simple, one-off cases.\n",{"id":970,"difficulty":208,"q":971,"a":972},"object-deps","Why does an object or array dependency re-run the effect every render?","Dependencies are compared by **reference** (`Object.is`). An object\u002Farray literal\ncreated during render is a **new reference each time**, so the effect sees a\n\"changed\" dependency on every render and re-runs endlessly.\n\n```jsx\n\u002F\u002F options is a new object every render -> effect runs every render\nconst options = { id }\nuseEffect(() => subscribe(options), [options])\n```\n\nFixes: depend on the **primitive** inside (`[id]`), build the object **inside**\nthe effect, or memoize it with `useMemo`. Same applies to functions passed as\ndeps — stabilize with `useCallback`.\n",{"id":974,"difficulty":208,"q":975,"a":976},"usecallback-dep","How does useCallback help with effect dependencies?","A function defined in a component body is recreated each render (new reference).\nIf an effect depends on it, the effect re-runs every render. `useCallback`\nreturns a **stable** function identity that only changes when its own deps do.\n\n```jsx\nconst load = useCallback(() => fetchData(id), [id])\nuseEffect(() => { load() }, [load]) \u002F\u002F re-runs only when id changes\n```\n\nWithout it, the effect would fire on every render. `useCallback` is mainly about\n**referential stability** for deps and memoized children — not raw speed.\n",{"id":978,"difficulty":121,"q":979,"a":980},"usememo-dep","How do you stabilize an object dependency with useMemo?","Wrap the object's creation in `useMemo` so it keeps the same reference until its\ninputs change, which keeps a dependent effect from re-running needlessly.\n\n```jsx\nconst filters = useMemo(() => ({ status, sort }), [status, sort])\nuseEffect(() => {\n  applyFilters(filters)\n}, [filters]) \u002F\u002F only when status or sort actually change\n```\n\nEquivalent alternative: skip the object and depend on `[status, sort]` directly.\nMemoize when the object must be passed around as a single value.\n",{"id":982,"difficulty":121,"q":983,"a":984},"polling-interval","How do you poll an API on an interval with useEffect?","Set up the interval in the effect and **clear it in cleanup** so it doesn't leak\nor duplicate when deps change or the component unmounts.\n\n```jsx\nuseEffect(() => {\n  const id = setInterval(() => refetch(), 5000)\n  return () => clearInterval(id)\n}, [refetch])\n```\n\nIf `refetch` isn't stable, wrap it in `useCallback` or you'll tear down and\nrecreate the interval each render. For variable intervals, a `useRef`-based\n`useInterval` hook is a common pattern.\n",{"id":986,"difficulty":208,"q":987,"a":988},"debounce-effect","How do you debounce a value with useEffect?","Start a timer in the effect that updates a \"debounced\" state, and **clear the\ntimer in cleanup** — so rapid changes keep resetting the timer until input\nsettles.\n\n```jsx\nconst [query, setQuery] = useState('')\nconst [debounced, setDebounced] = useState(query)\nuseEffect(() => {\n  const id = setTimeout(() => setDebounced(query), 300)\n  return () => clearTimeout(id) \u002F\u002F cancel if query changes again\n}, [query])\n\u002F\u002F run search on `debounced`, not `query`\n```\n\nEach keystroke re-runs the effect, whose cleanup cancels the previous pending\ntimer — so the update only fires after 300ms of quiet.\n",{"id":990,"difficulty":208,"q":991,"a":992},"subscribe-external","How do you subscribe to an external store from an effect?","Subscribe in the effect and **unsubscribe in cleanup**. For external stores,\nReact 18's `useSyncExternalStore` is the purpose-built hook (tear-free with\nconcurrent rendering), but a manual effect works for simple cases.\n\n```jsx\nuseEffect(() => {\n  const unsub = store.subscribe(() => setValue(store.get()))\n  setValue(store.get())   \u002F\u002F sync the initial value\n  return unsub            \u002F\u002F cleanup unsubscribes\n}, [store])\n```\n\nAlways read the current value once on subscribe so you don't miss a change that\nhappened between render and effect. Prefer `useSyncExternalStore` for shared\nstores.\n",{"id":994,"difficulty":121,"q":995,"a":996},"localstorage-sync","How do you sync state to localStorage with useEffect?","Write to `localStorage` in an effect that depends on the value, and read it lazily\nin the initializer so it's only parsed once.\n\n```jsx\nconst [theme, setTheme] = useState(() => localStorage.getItem('theme') ?? 'light')\nuseEffect(() => {\n  localStorage.setItem('theme', theme)\n}, [theme])\n```\n\nThe lazy initializer avoids reading storage on every render; the effect persists\nchanges. Guard `localStorage` access for SSR (it doesn't exist on the server) —\neffects don't run server-side, so this pattern is SSR-safe.\n",{"id":998,"difficulty":208,"q":999,"a":1000},"not-need-effect","When do you NOT need a useEffect?","Effects are for **synchronizing with external systems**, not for reacting to user\nevents or computing values. If something happens **because the user did\nsomething**, do it in the **event handler**, not an effect.\n\n```jsx\n\u002F\u002F effect reacting to a click that already happened\nuseEffect(() => { if (submitted) postData() }, [submitted])\n\u002F\u002F just do it in the handler\nfunction onSubmit() { postData() }\n```\n\n\"You Might Not Need an Effect\" (React docs): skip effects for derived data,\nevent responses, and resetting state on prop change (use `key` instead).\n",{"id":1002,"difficulty":121,"q":1003,"a":1004},"derive-not-effect","Why shouldn't you use an effect to compute derived state?","Storing computed data in state and syncing it with an effect causes an **extra\nrender** and risks the copy going stale. Just compute it during render.\n\n```jsx\n\u002F\u002F effect + extra state + extra render\nconst [full, setFull] = useState('')\nuseEffect(() => setFull(`${first} ${last}`), [first, last])\n\n\u002F\u002F derive in render (memoize only if expensive)\nconst full = `${first} ${last}`\n```\n\nAn effect-to-set-state for something derivable is a classic anti-pattern flagged\nin the React docs.\n",{"id":1006,"difficulty":208,"q":1007,"a":1008},"lint-suppress-danger","Why is disabling the exhaustive-deps lint dangerous?","Adding `\u002F\u002F eslint-disable-next-line react-hooks\u002Fexhaustive-deps` to silence a\nmissing dependency doesn't fix the bug — it hides it. The effect keeps using\n**stale** captured values and silently breaks when those values change.\n\n```jsx\n\u002F\u002F suppressing the warning to \"run once\" -> stale `userId`\nuseEffect(() => { fetchData(userId) }, []) \u002F\u002F eslint-disable-line\n```\n\nInstead, fix the root cause: include the dep, move logic inside the effect, use a\nfunctional setter, or stabilize the value with `useCallback`\u002F`useRef`. Suppress\nonly when you fully understand why it's safe.\n",{"id":1010,"difficulty":121,"q":1011,"a":1012},"cleanup-order2","In what order do cleanup and the next effect run?","On a dependency change, React runs the **previous** effect's cleanup **first**,\nthen runs the **new** effect. It never overlaps them.\n\n```jsx\nuseEffect(() => {\n  console.log('subscribe', id)\n  return () => console.log('unsubscribe', id)\n}, [id])\n\u002F\u002F id: 1 -> 2 logs: \"unsubscribe 1\" then \"subscribe 2\"\n```\n\nThis ordering guarantees you tear down the old subscription before setting up the\nnew one — no leaks, no duplicates. On unmount, only the last cleanup runs.\n",{"id":1014,"difficulty":208,"q":1015,"a":1016},"strict-double","Why does my effect run twice in development?","In React 18 **Strict Mode** (development only), React mounts each component,\n**unmounts it, and remounts it** to surface effects that aren't cleanup-safe. So\na mount effect + its cleanup fire twice.\n\n```jsx\nuseEffect(() => {\n  console.log('run')        \u002F\u002F logs twice in dev Strict Mode\n  return () => console.log('cleanup')\n}, [])\n```\n\nIt does **not** happen in production. If double-invocation breaks something\n(e.g. duplicate requests), that's a sign your effect needs proper cleanup or\nshouldn't be an effect at all — Strict Mode is doing its job.\n",{"id":1018,"difficulty":121,"q":1019,"a":1020},"infinite-effect-loop","What causes an infinite loop in useEffect?","Setting state in an effect whose dependency **changes as a result of that state**\n— or using an unstable object\u002Farray\u002Ffunction dependency created each render.\n\n```jsx\n\u002F\u002F sets data -> re-render -> new [] dep -> runs again -> loop\nuseEffect(() => setData(compute()), [{}])\n\n\u002F\u002F depends on the state it updates\nuseEffect(() => setCount(count + 1), [count])\n```\n\nFixes: stabilize deps (memoize objects\u002Ffunctions), depend on primitives, or use\na functional updater so the effect needn't depend on the state it sets.\n",{"id":1022,"difficulty":121,"q":1023,"a":1024},"async-effect-directly","Why can't the useEffect callback be async?","An `async` function returns a **Promise**, but React expects the effect callback\nto return **either nothing or a cleanup function**. Returning a Promise breaks\ncleanup. Define an async function **inside** and call it.\n\n```jsx\n\u002F\u002F async effect returns a Promise, not a cleanup fn\nuseEffect(async () => { await load() }, [])\n\n\u002F\u002F inner async function\nuseEffect(() => {\n  (async () => { await load() })()\n}, [])\n```\n\nThis keeps the return value available for cleanup while still letting you use\n`await` inside.\n",{"id":1026,"difficulty":96,"q":1027,"a":1028},"event-listener-effect","How do you add and remove a global event listener?","Add the listener in the effect and remove **the same function reference** in\ncleanup, so it's detached on unmount and not duplicated on re-runs.\n\n```jsx\nuseEffect(() => {\n  const onKey = e => { if (e.key === 'Escape') close() }\n  window.addEventListener('keydown', onKey)\n  return () => window.removeEventListener('keydown', onKey)\n}, [close])\n```\n\nThe cleanup must reference the **exact** function passed to `addEventListener`\n(an inline arrow in both calls won't match), which is why it's defined once\ninside the effect.\n",{"id":1030,"difficulty":121,"q":1031,"a":1032},"multiple-effects","Should you split logic into multiple effects?","Yes — use **separate effects for unrelated concerns**, each with its own\ndependency array. One effect per responsibility is clearer and avoids re-running\nunrelated logic when only one dependency changes.\n\n```jsx\nuseEffect(() => { document.title = title }, [title])     \u002F\u002F title sync\nuseEffect(() => {\n  const id = connect(roomId)\n  return () => disconnect(id)\n}, [roomId])                                             \u002F\u002F connection\n```\n\nDon't cram title updates and a subscription into one effect just because they're\nin the same component — split by what they synchronize.\n",{"id":1034,"difficulty":121,"q":1035,"a":1036},"effect-run-timing","When exactly does useEffect run relative to rendering?","The sequence is: React renders (calls your component) -> commits changes to the\nDOM -> **browser paints** -> *then* `useEffect` runs asynchronously. So effects\nnever block the visual update.\n\n```\nrender -> commit (DOM updated) -> paint -> useEffect\n```\n\nThis is why you shouldn't read final layout measurements that must be applied\n*before* paint in `useEffect` — use `useLayoutEffect` for those. For most work\n(fetching, subscriptions, logging), running after paint is exactly what you want.\n",{"id":1038,"difficulty":208,"q":1039,"a":1040},"ref-latest-in-effect","How do you read the latest prop\u002Fstate in a long-lived effect without re-subscribing?","Mirror the value in a `useRef` updated each render, and read `ref.current` inside\nthe effect. The effect can keep an empty dep array (set up once) yet always see\nfresh data.\n\n```jsx\nconst cbRef = useRef(onTick)\nuseEffect(() => { cbRef.current = onTick }) \u002F\u002F keep it current\nuseEffect(() => {\n  const id = setInterval(() => cbRef.current(), 1000)\n  return () => clearInterval(id)\n}, []) \u002F\u002F interval created once, always calls the latest onTick\n```\n\nThis is the core of the `useInterval`\u002F`useEventCallback` patterns — avoid stale\nclosures without tearing down the subscription on every change.\n",{"id":1042,"difficulty":96,"q":1043,"a":1044},"effect-once-mount","How do you run an effect only once when the component mounts?","Pass an **empty dependency array**. The effect runs after the first render and\nnever again (its cleanup runs on unmount).\n\n```jsx\nuseEffect(() => {\n  analytics.pageView()\n}, []) \u002F\u002F mount only\n```\n\nCaveat: in dev Strict Mode it runs twice, and the lint rule will warn if the\neffect actually *uses* props\u002Fstate you left out — so \"run once\" should genuinely\nhave no reactive dependencies.\n",{"id":1046,"difficulty":121,"q":1047,"a":1048},"dep-primitive-vs-object","Why are primitive dependencies safer than object dependencies?","Primitives (`string`, `number`, `boolean`) compare by **value**, so equal values\nare \"unchanged\" and the effect doesn't re-run. Objects\u002Farrays\u002Ffunctions compare\nby **reference**, so a freshly created one looks changed every render.\n\n```jsx\nuseEffect(() => {}, [userId])        \u002F\u002F re-runs only when the number changes\nuseEffect(() => {}, [{ userId }])    \u002F\u002F new object each render -> always re-runs\n```\n\nPrefer depending on the **specific primitive fields** you read rather than a\nwhole object — it's both safer and more precise.\n",{"id":1050,"difficulty":121,"q":1051,"a":1052},"server-no-run","Do effects run during server-side rendering?","No. `useEffect` (and `useLayoutEffect`) **do not run on the server** — they only\nrun in the browser after hydration. So effects are the right place for\nbrowser-only APIs (`window`, `localStorage`, `IntersectionObserver`).\n\n```jsx\nuseEffect(() => {\n  const mq = window.matchMedia('(min-width: 768px)') \u002F\u002F browser-only, safe here\n  \u002F\u002F ...\n}, [])\n```\n\nDon't access `window`\u002F`document` during render (it crashes SSR); defer it to an\neffect. `useLayoutEffect` additionally **warns** during SSR — guard or use\n`useEffect` there.\n",{"id":1054,"difficulty":208,"q":1055,"a":1056},"reset-on-prop-change","How do you reset state when a prop changes, without an effect?","Avoid the effect-that-resets-state pattern. Prefer **changing the `key`** to\nremount the subtree, or the conditional set-during-render pattern for partial\nresets.\n\n```jsx\n\u002F\u002F remount on userId change -> all state resets\n\u003CProfile key={userId} userId={userId} \u002F>\n\n\u002F\u002F partial reset during render (no effect, no extra paint)\nconst [prevId, setPrevId] = useState(id)\nif (id !== prevId) { setPrevId(id); setComment('') }\n```\n\nUsing an effect to watch the prop and call setters causes an extra render and is\nflagged as an anti-pattern in the docs.\n",{"id":1058,"difficulty":121,"q":1059,"a":1060},"cleanup-async-fetch","How does the ignore-flag pattern prevent setting state after unmount?","A boolean captured in the effect, flipped in cleanup, lets the async callback\ncheck whether the component is still mounted (and the request still current)\nbefore calling a setter.\n\n```jsx\nuseEffect(() => {\n  let ignore = false\n  load(id).then(data => { if (!ignore) setData(data) })\n  return () => { ignore = true } \u002F\u002F later resolution is ignored\n}, [id])\n```\n\nThis both prevents the \"can't update an unmounted component\" warning and avoids a\nstale earlier request overwriting newer data.\n",{"id":1062,"difficulty":208,"q":1063,"a":1064},"chained-effects-antipattern","Why are chains of effects that trigger each other an anti-pattern?","An effect that sets state, which triggers another effect that sets more state,\ncreates cascading renders that are hard to follow and inefficient — each step is\na separate render pass.\n\n```jsx\n\u002F\u002F effect -> setState -> effect -> setState ...\nuseEffect(() => setB(a + 1), [a])\nuseEffect(() => setC(b * 2), [b])\n```\n\nPrefer computing the values **together during render** (derive `b` and `c` from\n`a`), or do the multi-step update in a single **event handler**. Reserve effects\nfor genuine external synchronization, not internal state cascades.\n",{"id":1066,"difficulty":121,"q":1067,"a":1068},"effect-vs-render-purity","Why must side effects live in useEffect and not in the render body?","Rendering must be **pure** — given the same props\u002Fstate it returns the same JSX\nwith no side effects. React may call your component multiple times, bail out, or\ndiscard renders (concurrent features), so a side effect in render could run\nunexpectedly, repeatedly, or never.\n\n```jsx\n\u002F\u002F side effect during render — runs on every render, breaks purity\ndocument.title = title\n\u002F\u002F after commit, controlled by deps\nuseEffect(() => { document.title = title }, [title])\n```\n\nKeeping effects out of render is what lets React safely re-render and optimize.\n",{"id":1070,"difficulty":96,"q":1071,"a":1072},"analytics-on-change","How do you run code after a specific value changes?","Use an effect that **depends on that value** — it runs after each commit where\nthe value changed.\n\n```jsx\nuseEffect(() => {\n  analytics.track('step_changed', { step })\n}, [step])\n```\n\nThis is the hooks replacement for the class `setState` callback or\n`componentDidUpdate` comparisons: react to the new value in an effect keyed on\nit. Make sure the dependency is the exact value you're reacting to.\n",35,{"description":94},"React useEffect interview questions and answers — the dependency array, cleanup functions, effect timing and common mistakes.","react\u002Fhooks\u002Fuseeffect","useEffect","oiUYwCv6s2Efb1rS8-QaAuPr3zhWkCjNxhMOeRgrlQo",{"id":1080,"title":1081,"body":1082,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1086,"navigation":100,"order":11,"path":1087,"questions":1088,"questionsCount":416,"related":169,"seo":1165,"seoDescription":1166,"stem":1167,"subtopic":1168,"topic":72,"topicSlug":74,"updated":420,"__hash__":1169},"qa\u002Freact\u002Fpatterns\u002Frender-props-hoc.md","Render Props Hoc",{"type":91,"value":1083,"toc":1084},[],{"title":94,"searchDepth":11,"depth":11,"links":1085},[],{},"\u002Freact\u002Fpatterns\u002Frender-props-hoc",[1089,1093,1097,1101,1105,1109,1113,1117,1121,1125,1129,1133,1137,1141,1145,1149,1153,1157,1161],{"id":1090,"difficulty":96,"q":1091,"a":1092},"what-is-render-props-pattern","What is the render props pattern and what problem does it solve?","The **render props** pattern is a technique for sharing stateful logic between components\nby passing a **function as a prop**. The host component manages state and calls that\nfunction with the state as arguments, letting the consumer decide what to render.\n\nThe problem it solves is **logic reuse without inheritance**. Before hooks, if two\ncomponents needed the same piece of state (mouse position, window size, data fetching\nstatus) you had three options: copy-paste the logic, lift state up (coupling unrelated\ncomponents), or use render props \u002F HOCs. Render props kept the logic encapsulated in\none place while giving the consumer full rendering control.\n\n```jsx\n\u002F\u002F DataFetcher owns fetching logic; caller decides the UI\nfunction DataFetcher({ url, render }) {\n  const [data, setData] = React.useState(null);\n  const [loading, setLoading] = React.useState(true);\n\n  React.useEffect(() => {\n    fetch(url)\n      .then(r => r.json())\n      .then(d => { setData(d); setLoading(false); });\n  }, [url]);\n\n  \u002F\u002F Delegates rendering entirely to the caller\n  return render({ data, loading });\n}\n\n\u002F\u002F Usage — caller controls the UI\n\u003CDataFetcher\n  url=\"\u002Fapi\u002Fusers\"\n  render={({ data, loading }) =>\n    loading ? \u003CSpinner \u002F> : \u003CUserList users={data} \u002F>\n  }\n\u002F>\n```\n\n**Rule of thumb:** Render props shine when you want to share complex stateful behaviour\nwhile letting each consumer render something different.\n",{"id":1094,"difficulty":96,"q":1095,"a":1096},"function-as-child-vs-render-prop","What is the function-as-child pattern and how does it differ from an explicit render prop?","**Function-as-child** (also called \"children as a function\") is just a render prop\nwhere the prop name is `children` instead of a custom name like `render`. The component\ncalls `props.children(...)` exactly as it would call `props.render(...)`.\n\nThe only differences are:\n- Syntactic: the function lives inside the JSX tag body, which reads more naturally\n  for simple cases.\n- Discoverability: explicit render props (`render`, `renderHeader`, etc.) are visible\n  in prop-types\u002FTypeScript signatures; `children` can surprise readers if they expect\n  a ReactNode.\n\n```jsx\n\u002F\u002F Explicit render prop\n\u003CMouse render={({ x, y }) => \u003CCursor x={x} y={y} \u002F>} \u002F>\n\n\u002F\u002F Function-as-child — identical behaviour, different syntax\n\u003CMouse>\n  {({ x, y }) => \u003CCursor x={x} y={y} \u002F>}\n\u003C\u002FMouse>\n\n\u002F\u002F Inside Mouse — both approaches call the same way\nfunction Mouse({ children }) {\n  const [pos, setPos] = React.useState({ x: 0, y: 0 });\n  return (\n    \u003Cdiv onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>\n      {children(pos)}  {\u002F* or props.render(pos) *\u002F}\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Use an explicit render prop when you need multiple render slots\n(`renderHeader`, `renderFooter`); use `children` when there is only one slot and\nthe JSX nesting reads naturally.\n",{"id":1098,"difficulty":96,"q":1099,"a":1100},"what-is-hoc","What is a Higher-Order Component (HOC) and what is its signature?","A **Higher-Order Component** is a function that takes a component and returns a new,\nenhanced component. It is the React adaptation of the higher-order function concept —\njust as `Array.map` takes a function and returns a new array, an HOC takes a component\nand returns a component.\n\nThe canonical signature is:\n\n```ts\n\u002F\u002F Generic signature: WrappedComponent in, EnhancedComponent out\nfunction withSomething\u003CP extends object>(\n  WrappedComponent: React.ComponentType\u003CP>\n): React.ComponentType\u003CP & InjectedProps> {\n\n  function WithSomething(props: P) {\n    const injected = useSomethingLogic(); \u002F\u002F HOC-owned logic\n    return \u003CWrappedComponent {...props} {...injected} \u002F>;\n  }\n\n  \u002F\u002F Naming convention: prefix with \"with\"\n  WithSomething.displayName =\n    `WithSomething(${WrappedComponent.displayName ?? WrappedComponent.name ?? 'Component'})`;\n\n  return WithSomething;\n}\n```\n\nKey points:\n- The **naming convention** is `withXxx` (camelCase, \"with\" prefix) — e.g., `withAuth`,\n  `withTheme`, `withLogger`.\n- Set `displayName` so React DevTools shows `WithAuth(Dashboard)` rather than an\n  anonymous component.\n- The HOC **does not mutate** the wrapped component; it wraps it.\n\n**Rule of thumb:** If a function takes a component and returns a component, it is an\nHOC; name it `withXxx` and set `displayName`.\n",{"id":1102,"difficulty":121,"q":1103,"a":1104},"hoc-composition","How do you compose multiple HOCs and what utility makes that cleaner?","You can nest HOC calls directly, but deep nesting reads inside-out and is hard to\nmaintain. The standard solution is a **`compose` utility** (available in lodash, Redux,\nRamda, or trivial to write) that applies functions right-to-left so the reading order\nmatches the wrapping order.\n\n```js\n\u002F\u002F Naive nesting — reads inside-out, breaks easily when reordering\nexport default withLogger(withAuth(withTheme(MyComponent)));\n\n\u002F\u002F compose applies right-to-left: theme → auth → logger (outermost last)\nimport { compose } from 'redux'; \u002F\u002F or lodash\u002Ffp\n\nconst enhance = compose(\n  withLogger,   \u002F\u002F applied last (outermost)\n  withAuth,     \u002F\u002F applied second\n  withTheme,    \u002F\u002F applied first (innermost, closest to component)\n);\n\nexport default enhance(MyComponent);\n\n\u002F\u002F Minimal compose implementation for reference\nconst compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);\n```\n\nOrder matters: the outermost HOC's props arrive first. If `withAuth` reads a `theme`\nprop injected by `withTheme`, then `withTheme` must be applied before `withAuth`\n(listed after it in `compose`).\n\n**Rule of thumb:** Use `compose` when stacking three or more HOCs; keep the list in\ninside-out (most-specific-first) order so execution matches your mental model.\n",{"id":1106,"difficulty":121,"q":1107,"a":1108},"cross-cutting-concerns-hoc","Name three cross-cutting concerns that HOCs handle well and explain one in detail.","Classic cross-cutting concerns suited to HOCs:\n\n1. **Authentication \u002F authorisation guards** — redirect unauthenticated users before\n   rendering a page component.\n2. **Analytics \u002F logging** — record component mount, unmount, and prop changes without\n   touching business logic components.\n3. **Error boundaries** — wrap any component with standardised error UI.\n\n`withAuth` example in detail:\n\n```jsx\n\u002F\u002F withAuth.jsx — auth guard HOC\nimport { Navigate } from 'react-router-dom';\nimport { useAuth } from '..\u002Fcontext\u002FAuthContext';\n\nfunction withAuth(WrappedComponent, requiredRole = null) {\n  function WithAuth(props) {\n    const { user, loading } = useAuth();\n\n    if (loading) return \u003CSpinner \u002F>;\n    if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n    if (requiredRole && user.role !== requiredRole)\n      return \u003CNavigate to=\"\u002Fforbidden\" replace \u002F>;\n\n    \u002F\u002F User is authenticated (and has the right role) — render component\n    return \u003CWrappedComponent {...props} \u002F>;\n  }\n\n  WithAuth.displayName = `WithAuth(${WrappedComponent.displayName ?? WrappedComponent.name})`;\n  return WithAuth;\n}\n\n\u002F\u002F Usage\nexport default withAuth(AdminDashboard, 'admin');\n```\n\n**Rule of thumb:** HOCs are ideal for cross-cutting concerns that need to intercept\nrendering (guards, error boundaries) or observe lifecycle events (logging) without\naltering the wrapped component's own logic.\n",{"id":1110,"difficulty":121,"q":1111,"a":1112},"prop-collision-hoc","What is prop collision in HOCs and how do you prevent it?","**Prop collision** occurs when an HOC injects a prop whose name clashes with a prop\nthe wrapped component (or a parent) already uses. The HOC's value silently wins because\nit is spread last (or first), overwriting the intended value and causing subtle bugs.\n\nPrevention strategies:\n\n```jsx\n\u002F\u002F BAD — HOC spreads injected props after ownProps; \"user\" collision\nfunction withUser(WrappedComponent) {\n  return function(props) {\n    const user = useCurrentUser();\n    \u002F\u002F If parent passes user={guestUser}, HOC's user overwrites it silently\n    return \u003CWrappedComponent {...props} user={user} \u002F>;\n  };\n}\n\n\u002F\u002F GOOD — namespace injected props under a dedicated key\nfunction withUser(WrappedComponent) {\n  return function({ injectedUser, ...ownProps }) {\n    const user = useCurrentUser();\n    \u002F\u002F Injected prop has a unique name; no risk of shadowing parent props\n    return \u003CWrappedComponent {...ownProps} currentUser={user} \u002F>;\n  };\n}\n\n\u002F\u002F BEST — use TypeScript to make the contract explicit\ntype WithUserProps = { currentUser: User };\nfunction withUser\u003CP extends WithUserProps>(\n  WrappedComponent: React.ComponentType\u003CP>\n) { \u002F* ... *\u002F }\n```\n\nOther strategies: document all injected prop names, prefix them (`_injectedUser`),\nor prefer hooks (which do not pollute the prop namespace at all).\n\n**Rule of thumb:** Never spread HOC-injected props with generic names; use descriptive,\nnamespaced names and make them explicit in TypeScript types.\n",{"id":1114,"difficulty":121,"q":1115,"a":1116},"why-hooks-replaced-render-props-hocs","Why did custom hooks largely replace render props and HOCs?","Custom hooks solve the same logic-reuse problem without the downsides of both patterns:\n\n| Issue | Render props \u002F HOCs | Custom hooks |\n|---|---|---|\n| **Extra DOM nodes** | Yes — every HOC \u002F render-prop host adds a wrapper in the tree | No — hooks run inside the calling component |\n| **Prop drilling \u002F collision** | HOCs inject props that can clash | Hooks return values via destructuring — no prop namespace |\n| **Composability** | HOCs compose left-to-right but reading order is confusing | Just call multiple hooks sequentially |\n| **TypeScript** | Generics + conditional types needed for good inference | Plain function return types — straightforward |\n| **DevTools clarity** | Deep \"wrapper hell\" in component tree | Flat component tree |\n\n```jsx\n\u002F\u002F Pre-hooks: render prop for mouse position\n\u003CMouseTracker render={({ x, y }) => \u003CCrosshair x={x} y={y} \u002F>} \u002F>\n\n\u002F\u002F Post-hooks: extract into a custom hook — same logic, no wrapper\nfunction useMousePosition() {\n  const [pos, setPos] = React.useState({ x: 0, y: 0 });\n  React.useEffect(() => {\n    const handler = e => setPos({ x: e.clientX, y: e.clientY });\n    window.addEventListener('mousemove', handler);\n    return () => window.removeEventListener('mousemove', handler);\n  }, []);\n  return pos;\n}\n\nfunction Crosshair() {\n  const { x, y } = useMousePosition(); \u002F\u002F clean, no wrapper\n  return \u003Cdiv style={{ top: y, left: x }} \u002F>;\n}\n```\n\n**Rule of thumb:** Reach for a custom hook first; only use render props or HOCs when\nyou need to control rendering structure (render prop) or integrate with class components\nor third-party code that cannot use hooks (HOC).\n",{"id":1118,"difficulty":121,"q":1119,"a":1120},"when-render-props-still-right-tool","When are render props still the right tool even in a hooks world?","Render props remain the best choice in several scenarios:\n\n1. **Controlled render delegation** — when the parent needs to decide *what* renders\n   inside a complex host (e.g., a virtualised list that injects row index\u002Fstyle and\n   expects the caller to return the row JSX).\n2. **Class component consumers** — hooks cannot run inside class components; an HOC\n   or render-prop component is the only way to feed hook-based logic into them.\n3. **Library APIs** — `react-router` (`\u003CRoute render>` in v5), `formik` (`\u003CField>`),\n   and `react-window` (`\u003CFixedSizeList>`) expose render props because they cannot\n   know your render tree ahead of time.\n\n```jsx\n\u002F\u002F react-window — render prop is the correct API; hooks cannot replace it here\nimport { FixedSizeList } from 'react-window';\n\nfunction VirtualList({ items }) {\n  const Row = ({ index, style }) => (\n    \u002F\u002F style MUST be applied for virtualisation to work\n    \u003Cdiv style={style}>{items[index].name}\u003C\u002Fdiv>\n  );\n\n  return (\n    \u003CFixedSizeList height={400} itemCount={items.length} itemSize={35} width=\"100%\">\n      {Row}\n    \u003C\u002FFixedSizeList>\n  );\n}\n```\n\n**Rule of thumb:** Render props are still the right tool when a library needs you to\nsupply a JSX factory, or when the component consuming your logic is a class component.\n",{"id":1122,"difficulty":208,"q":1123,"a":1124},"static-methods-hocs","Why do HOCs lose static methods from the wrapped component and how do you fix it?","A wrapped component's **static methods** (e.g., `getLayout`, `fetchData`,\n`propTypes`) are defined on the original component object. The HOC returns a *new*\nfunction object that does not automatically copy those statics, so callers that\nlook up `EnhancedComponent.getLayout` find `undefined`.\n\nThe fix is **`hoist-non-react-statics`**, a small utility that copies all non-React\nstatics from the wrapped component to the wrapper:\n\n```jsx\nimport hoistNonReactStatics from 'hoist-non-react-statics';\n\nfunction withLogger(WrappedComponent) {\n  function WithLogger(props) {\n    React.useEffect(() => { console.log('mounted'); }, []);\n    return \u003CWrappedComponent {...props} \u002F>;\n  }\n\n  WithLogger.displayName =\n    `WithLogger(${WrappedComponent.displayName ?? WrappedComponent.name})`;\n\n  \u002F\u002F Copy getLayout, fetchData, propTypes, etc. from original\n  hoistNonReactStatics(WithLogger, WrappedComponent);\n\n  return WithLogger;\n}\n\n\u002F\u002F Statics survive the wrapping\nWithLogger.getLayout === WrappedComponent.getLayout; \u002F\u002F true\n```\n\nReact-specific statics (`defaultProps`, `propTypes`, `contextType`, `displayName`,\n`getDerivedStateFromProps`, etc.) are intentionally **not** hoisted — the library\nhas a hardcoded exclusion list for them.\n\n**Rule of thumb:** Always call `hoistNonReactStatics(Wrapper, WrappedComponent)`\ninside every HOC factory so page-level statics (especially Next.js `getLayout`) are\nnot silently dropped.\n",{"id":1126,"difficulty":208,"q":1127,"a":1128},"forwardref-in-hocs","How do you forward refs through a HOC?","HOCs break `ref` forwarding by default because `ref` is not a real prop — it is\nhandled by React's reconciler and does not appear in `props`. A ref attached to\nthe HOC wrapper points at the wrapper, not the underlying DOM node or component.\n\nThe fix is `React.forwardRef`:\n\n```jsx\nfunction withFocusRing(WrappedComponent) {\n  \u002F\u002F forwardRef creates a component that passes its ref down\n  const WithFocusRing = React.forwardRef(function(props, ref) {\n    const [focused, setFocused] = React.useState(false);\n    return (\n      \u003Cdiv className={focused ? 'ring' : ''}>\n        \u003CWrappedComponent\n          {...props}\n          ref={ref}           \u002F\u002F forward the ref to the real component\n          onFocus={() => setFocused(true)}\n          onBlur={() => setFocused(false)}\n        \u002F>\n      \u003C\u002Fdiv>\n    );\n  });\n\n  WithFocusRing.displayName =\n    `WithFocusRing(${WrappedComponent.displayName ?? WrappedComponent.name})`;\n\n  hoistNonReactStatics(WithFocusRing, WrappedComponent);\n  return WithFocusRing;\n}\n\n\u002F\u002F Caller gets a ref to the real input, not the wrapper div\nconst EnhancedInput = withFocusRing(React.forwardRef((props, ref) =>\n  \u003Cinput ref={ref} {...props} \u002F>\n));\nconst ref = React.createRef();\n\u003CEnhancedInput ref={ref} \u002F>;\n```\n\n**Rule of thumb:** Wrap the inner component in `React.forwardRef` and thread the\n`ref` argument through to `WrappedComponent`; always pair this with `hoistNonReactStatics`.\n",{"id":1130,"difficulty":121,"q":1131,"a":1132},"hoc-performance-implications","What are the performance implications of HOCs and render props?","Both patterns can introduce unnecessary re-renders if not handled carefully.\n\n**HOC issues:**\n- Each HOC in a composition chain is a separate React component — `React.memo` on\n  the inner component does not prevent the wrapper from re-rendering.\n- If the HOC creates a new object or function on every render and passes it as a\n  prop, the wrapped component will see a new reference and re-render even if the\n  underlying data did not change.\n\n**Render prop issues (the classic \"always re-renders\" problem):**\n```jsx\n\u002F\u002F BAD — inline arrow function creates a new function reference every render\n\u002F\u002F → Mouse re-renders, calls render(), child always re-renders\n\u003CMouse render={({ x, y }) => \u003CCrosshair x={x} y={y} \u002F>} \u002F>\n\n\u002F\u002F GOOD — stable function reference with useCallback (or define outside render)\nfunction App() {\n  const renderCrosshair = React.useCallback(\n    ({ x, y }) => \u003CCrosshair x={x} y={y} \u002F>,\n    [] \u002F\u002F stable — no deps\n  );\n  return \u003CMouse render={renderCrosshair} \u002F>;\n}\n```\n\nFor HOCs, apply `React.memo` to *each layer* independently, or memoize injected\nvalues inside the HOC with `useMemo`\u002F`useCallback`.\n\n**Rule of thumb:** Stabilise function references passed as render props with\n`useCallback`; inside HOCs, memoize injected values to avoid cascading re-renders.\n",{"id":1134,"difficulty":208,"q":1135,"a":1136},"typescript-generics-hoc","How do you type a HOC in TypeScript so that the wrapped component's props are preserved?","The key is using **generics** to capture the wrapped component's prop type and subtract\nthe injected props so callers do not have to re-supply them.\n\n```tsx\n\u002F\u002F Props injected by the HOC\ninterface WithAuthProps {\n  currentUser: User;\n}\n\n\u002F\u002F P = full props of wrapped component\n\u002F\u002F We subtract WithAuthProps so callers don't pass currentUser manually\nfunction withAuth\u003CP extends WithAuthProps>(\n  WrappedComponent: React.ComponentType\u003CP>\n): React.ComponentType\u003COmit\u003CP, keyof WithAuthProps>> {\n\n  function WithAuth(props: Omit\u003CP, keyof WithAuthProps>) {\n    const { user } = useAuth();\n    if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n\n    \u002F\u002F Cast needed because TS can't fully verify the Omit + spread\n    return \u003CWrappedComponent {...(props as P)} currentUser={user} \u002F>;\n  }\n\n  WithAuth.displayName =\n    `WithAuth(${WrappedComponent.displayName ?? WrappedComponent.name ?? 'Component'})`;\n\n  hoistNonReactStatics(WithAuth, WrappedComponent);\n  return WithAuth;\n}\n\n\u002F\u002F Usage — TypeScript knows currentUser is supplied by HOC, not by caller\ninterface DashboardProps extends WithAuthProps { title: string; }\nfunction Dashboard({ currentUser, title }: DashboardProps) { \u002F* ... *\u002F }\n\nconst ProtectedDashboard = withAuth(Dashboard);\n\u003CProtectedDashboard title=\"Home\" \u002F> \u002F\u002F ✓ — no currentUser required\n```\n\n**Rule of thumb:** Use `P extends InjectedProps` on the generic and `Omit\u003CP, keyof InjectedProps>`\non the output type so the HOC's additions are invisible to callers.\n",{"id":1138,"difficulty":121,"q":1139,"a":1140},"testing-hoc-wrapped-components","What are two strategies for testing a HOC-wrapped component?","**Strategy 1 — Test the wrapped component in isolation.**\nExport the unwrapped component as a named export alongside the default HOC-wrapped\nexport. In tests, import the unwrapped version and pass the injected props manually.\nThis keeps unit tests free of HOC side-effects.\n\n**Strategy 2 — Mock the HOC's dependencies and test the full composed component.**\nUseful for integration tests that need to verify the HOC's interception logic (e.g.,\nthat `withAuth` redirects unauthenticated users).\n\n```tsx\n\u002F\u002F userList.tsx — dual export pattern\nexport function UserList({ currentUser }: WithAuthProps & { currentUser: User }) {\n  return \u003Cdiv>{currentUser.name}\u003C\u002Fdiv>;\n}\nexport default withAuth(UserList); \u002F\u002F default = enhanced\n\n\u002F\u002F userList.test.tsx\nimport { UserList } from '.\u002FuserList'; \u002F\u002F named — no HOC\nimport { render, screen } from '@testing-library\u002Freact';\n\ntest('renders user name', () => {\n  render(\u003CUserList currentUser={{ name: 'Alice', role: 'user' }} \u002F>);\n  expect(screen.getByText('Alice')).toBeInTheDocument();\n});\n\n\u002F\u002F Integration test — mock auth context\nimport WrappedUserList from '.\u002FuserList'; \u002F\u002F default — HOC included\ntest('redirects when unauthenticated', () => {\n  mockUseAuth({ user: null, loading: false });\n  render(\u003CWrappedUserList \u002F>, { wrapper: MemoryRouterWrapper });\n  expect(mockNavigate).toHaveBeenCalledWith('\u002Flogin', expect.anything());\n});\n```\n\n**Rule of thumb:** Always export the base component as a named export so unit tests\ncan bypass the HOC; use integration tests only when you need to test the HOC's own\nbehaviour.\n",{"id":1142,"difficulty":96,"q":1143,"a":1144},"render-props-vs-hoc-choice","When would you choose a render prop over a HOC, and vice-versa?","The decision hinges on who controls the rendered output and how the logic is consumed.\n\n| Choose **render prop** when… | Choose **HOC** when… |\n|---|---|\n| The consumer needs to render different UI per use-site | Every consumer renders roughly the same structure |\n| You want explicit, visible data flow in JSX | You want transparent wrapping (consumer doesn't change) |\n| You need to compose render slots (header, body, footer) | You need to decorate a whole page\u002Froute component |\n| The consuming component is a function component | You need to wrap class components with consistent behaviour |\n\n```jsx\n\u002F\u002F Render prop — consumer decides the UI\n\u003CToggle>\n  {({ on, toggle }) =>\n    on\n      ? \u003CButton onClick={toggle}>Turn Off\u003C\u002FButton>\n      : \u003CButton onClick={toggle}>Turn On\u003C\u002FButton>\n  }\n\u003C\u002FToggle>\n\n\u002F\u002F HOC — consumer is unaware of the wrapping\nconst AnalyticsButton = withPageViewLogger(Button);\n\u002F\u002F Button code is unchanged; analytics are injected invisibly\n\u003CAnalyticsButton onClick={handleClick}>Buy Now\u003C\u002FAnalyticsButton>\n```\n\n**Rule of thumb:** Use render props when the consumer owns the output markup; use\nHOCs when you want to transparently add behaviour to an existing component.\n",{"id":1146,"difficulty":96,"q":1147,"a":1148},"wrapper-hell","What is \"wrapper hell\" and how do you recognise it in React DevTools?","**Wrapper hell** (also called \"HOC hell\" or \"pyramid of doom\") describes a component\ntree where many HOC layers surround a single component, making DevTools output nearly\nunreadable and stack traces hard to follow.\n\nIn React DevTools you see something like:\n\n```\n\u002F\u002F Component tree with 5 HOCs — hard to navigate\n\u003CWithRouter>\n  \u003CWithTheme>\n    \u003CWithAuth>\n      \u003CWithLogger>\n        \u003CWithErrorBoundary>\n          \u003CDashboard \u002F>\n        \u003C\u002FWithErrorBoundary>\n      \u003C\u002FWithLogger>\n    \u003C\u002FWithAuth>\n  \u003C\u002FWithTheme>\n\u003C\u002FWithRouter>\n```\n\nSolutions:\n1. **Replace with hooks** — collapse all five wrappers into hook calls inside `Dashboard`.\n2. **Use `compose`** — at least the source code reads linearly even if the tree is deep.\n3. **Set meaningful `displayName`** on each HOC so DevTools shows `WithAuth(Dashboard)`.\n\n```jsx\n\u002F\u002F After refactoring to hooks — flat tree, same logic\nfunction Dashboard() {\n  const { params } = useRouter();\n  const theme = useTheme();\n  const { user } = useAuth();\n  usePageLogger();\n  useErrorBoundary();\n  \u002F\u002F ...\n}\n```\n\n**Rule of thumb:** If DevTools shows more than two or three wrapper components around\na single leaf, consider collapsing the HOC stack into custom hooks.\n",{"id":1150,"difficulty":121,"q":1151,"a":1152},"hoc-vs-hooks-class-components","Can hooks fully replace HOCs for class components? What is the recommended migration path?","**No** — hooks cannot run inside class components. An HOC (or render prop) remains\nthe only way to inject hook-based logic into a class component. This matters for\nlegacy codebases or third-party libraries still using class components.\n\nThe recommended migration path is **incremental**:\n\n```jsx\n\u002F\u002F Step 1 — write the logic as a hook\nfunction useTheme() {\n  return React.useContext(ThemeContext);\n}\n\n\u002F\u002F Step 2 — create a thin HOC that calls the hook and injects the result\n\u002F\u002F           This bridges class components without rewriting them\nfunction withTheme(WrappedComponent) {\n  function WithTheme(props) {\n    const theme = useTheme(); \u002F\u002F hook runs here (function component)\n    return \u003CWrappedComponent {...props} theme={theme} \u002F>;\n  }\n  WithTheme.displayName = `WithTheme(${WrappedComponent.name})`;\n  return WithTheme;\n}\n\n\u002F\u002F Step 3 — class component uses the HOC today\nclass Toolbar extends React.Component {\n  render() {\n    return \u003Cdiv style={{ background: this.props.theme.bg }}>…\u003C\u002Fdiv>;\n  }\n}\nexport default withTheme(Toolbar);\n\n\u002F\u002F Step 4 — when Toolbar is converted to a function component, drop the HOC\n\u002F\u002F           and call useTheme() directly\n```\n\n**Rule of thumb:** Write all new logic as hooks; wrap in a thin HOC only to bridge\nclass components; remove the HOC shim when the class component is converted.\n",{"id":1154,"difficulty":96,"q":1155,"a":1156},"hoc-display-name-convention","Why is setting displayName on a HOC important and what is the correct format?","React uses `displayName` (or the function name as a fallback) when rendering component\ntrees in **DevTools** and in **error stack traces**. Without it, every HOC-wrapped\ncomponent shows as `Anonymous` or just the HOC function name, making debugging very\nhard when multiple HOCs are stacked.\n\nThe standard format is `WithXxx(OriginalName)`:\n\n```jsx\nfunction withSubscription(WrappedComponent) {\n  function WithSubscription(props) {\n    \u002F\u002F ...\n    return \u003CWrappedComponent {...props} \u002F>;\n  }\n\n  \u002F\u002F Correct format: WithHocName(WrappedName)\n  WithSubscription.displayName =\n    `WithSubscription(${\n      WrappedComponent.displayName  \u002F\u002F already set display name\n      ?? WrappedComponent.name      \u002F\u002F function\u002Fclass name\n      ?? 'Component'               \u002F\u002F last resort fallback\n    })`;\n\n  return WithSubscription;\n}\n\n\u002F\u002F DevTools now shows: WithSubscription(Dashboard)\n\u002F\u002F instead of:         WithSubscription or Anonymous\n```\n\nSome teams use a `getDisplayName` helper to deduplicate the ternary chain across\nall HOCs in a codebase.\n\n**Rule of thumb:** Always set `displayName` using the `WithXxx(InnerName)` format;\ncheck all three sources (`displayName`, `name`, fallback) in that priority order.\n",{"id":1158,"difficulty":208,"q":1159,"a":1160},"render-props-error-boundaries","Can you use render props with error boundaries? How?","Yes. An **error boundary** must be a class component (React's `componentDidCatch`\nAPI is not yet available as a hook), but you can expose its caught error via a\nrender prop so the caller controls the fallback UI — combining the two patterns.\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { error: null };\n\n  static getDerivedStateFromError(error) {\n    return { error };\n  }\n\n  componentDidCatch(error, info) {\n    reportError(error, info); \u002F\u002F side-effect: logging service\n  }\n\n  render() {\n    const { error } = this.state;\n    const { children, fallback } = this.props;\n\n    if (error) {\n      \u002F\u002F Delegate fallback UI to the caller via render prop\n      return typeof fallback === 'function'\n        ? fallback(error)       \u002F\u002F render prop form\n        : fallback ?? \u003Cp>Something went wrong.\u003C\u002Fp>;\n    }\n\n    return children;\n  }\n}\n\n\u002F\u002F Caller controls the fallback UI\n\u003CErrorBoundary fallback={err => \u003CErrorPage message={err.message} \u002F>}>\n  \u003CUserProfile userId={id} \u002F>\n\u003C\u002FErrorBoundary>\n```\n\nThis pattern is exactly how the popular `react-error-boundary` library works\ninternally (`renderError` \u002F `FallbackComponent` prop).\n\n**Rule of thumb:** Error boundary logic stays in a class component; expose the\nfallback slot as a render prop so each call site can render appropriate recovery UI.\n",{"id":1162,"difficulty":208,"q":1163,"a":1164},"render-prop-pure-component-issue","Why can inline render prop functions cause issues with PureComponent or React.memo and how do you fix it?","`React.PureComponent` and `React.memo` perform a **shallow comparison** of props.\nAn inline arrow function creates a new function object on every parent render, so\nthe render-prop component always sees a changed `render` prop — the shallow compare\nfails, PureComponent's optimisation is bypassed, and the component re-renders\nunconditionally.\n\n```jsx\nclass Mouse extends React.PureComponent {\n  \u002F\u002F shallow compare on props.render — always a new reference → always re-renders\n  render() { return this.props.render(this.state); }\n}\n\n\u002F\u002F BAD — new arrow function on every render of App\nclass App extends React.Component {\n  render() {\n    return \u003CMouse render={pos => \u003CCat {...pos} \u002F>} \u002F>;  \u002F\u002F new fn each time!\n  }\n}\n\n\u002F\u002F GOOD — define the render function as an instance method; reference is stable\nclass App extends React.Component {\n  renderCat = pos => \u003CCat {...pos} \u002F>;  \u002F\u002F stable class field\n\n  render() {\n    return \u003CMouse render={this.renderCat} \u002F>;\n  }\n}\n\n\u002F\u002F GOOD (function component equivalent) — useCallback for stable reference\nfunction App() {\n  const renderCat = React.useCallback(pos => \u003CCat {...pos} \u002F>, []);\n  return \u003CMouse render={renderCat} \u002F>;\n}\n```\n\n**Rule of thumb:** Never pass an inline arrow function as a render prop to a\n`PureComponent` or `memo`-wrapped component; stabilise the reference with\n`useCallback` or a class instance method.\n",{"description":94},"React render props and HOC interview questions — function-as-child, higher-order components, withAuth, cross-cutting concerns, hooks vs HOCs comparison.","react\u002Fpatterns\u002Frender-props-hoc","Render Props & HOCs","5b8a_FFY9oOxE5j6YoohtNraMG4vZoSdafVNh58xkH8",{"id":1171,"title":1172,"body":1173,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1177,"navigation":100,"order":11,"path":1179,"questions":1180,"questionsCount":1237,"related":169,"seo":1238,"seoDescription":1239,"stem":1240,"subtopic":1241,"topic":45,"topicSlug":47,"updated":420,"__hash__":1242},"qa\u002Freact\u002Frendering-and-performance\u002Freact-memo.md","React Memo",{"type":91,"value":1174,"toc":1175},[],{"title":94,"searchDepth":11,"depth":11,"links":1176},[],{"subtopicSlug":1178},"react-memo","\u002Freact\u002Frendering-and-performance\u002Freact-memo",[1181,1185,1189,1193,1197,1201,1205,1209,1213,1217,1221,1225,1229,1233],{"id":1182,"difficulty":96,"q":1183,"a":1184},"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":1186,"difficulty":96,"q":1187,"a":1188},"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":1190,"difficulty":121,"q":1191,"a":1192},"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":1194,"difficulty":121,"q":1195,"a":1196},"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":1198,"difficulty":121,"q":1199,"a":1200},"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":1202,"difficulty":96,"q":1203,"a":1204},"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":1206,"difficulty":208,"q":1207,"a":1208},"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":1210,"difficulty":208,"q":1211,"a":1212},"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":1214,"difficulty":121,"q":1215,"a":1216},"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":1218,"difficulty":96,"q":1219,"a":1220},"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":1222,"difficulty":121,"q":1223,"a":1224},"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":1226,"difficulty":121,"q":1227,"a":1228},"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":1230,"difficulty":121,"q":1231,"a":1232},"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":1234,"difficulty":208,"q":1235,"a":1236},"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":94},"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":1244,"title":1245,"body":1246,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1250,"navigation":100,"order":11,"path":1251,"questions":1252,"questionsCount":589,"related":169,"seo":1332,"seoDescription":1333,"stem":1334,"subtopic":1335,"topic":54,"topicSlug":56,"updated":420,"__hash__":1336},"qa\u002Freact\u002Frouting\u002Fdynamic-nested-routes.md","Dynamic Nested Routes",{"type":91,"value":1247,"toc":1248},[],{"title":94,"searchDepth":11,"depth":11,"links":1249},[],{},"\u002Freact\u002Frouting\u002Fdynamic-nested-routes",[1253,1257,1261,1265,1269,1273,1277,1281,1284,1288,1292,1296,1300,1304,1308,1312,1316,1320,1324,1328],{"id":1254,"difficulty":96,"q":1255,"a":1256},"dynamic-segment-syntax","How do you define a dynamic URL segment in React Router v6, and how do you read it inside the component?","A **dynamic segment** is a path token prefixed with `:`. React Router captures whatever the user types in that position and makes it available via **`useParams()`**, which returns an object keyed by segment name.\n\n```jsx\n\u002F\u002F Route definition\n\u003CRoute path=\"\u002Fusers\u002F:userId\" element={\u003CUserProfile \u002F>} \u002F>\n\n\u002F\u002F Inside UserProfile\nimport { useParams } from 'react-router-dom';\n\nfunction UserProfile() {\n  const { userId } = useParams(); \u002F\u002F e.g. \"42\"\n  \u002F\u002F userId is always a string — coerce if you need a number\n  return \u003Ch1>User {userId}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Name segments descriptively (`:userId`, not `:id`) so reading `useParams()` reads like documentation.\n",{"id":1258,"difficulty":96,"q":1259,"a":1260},"params-always-strings","What is the type of a route param returned by useParams, and what bug does that cause?","**`useParams()` always returns strings**, even when the URL contains what looks like a number. This is a common source of subtle bugs when comparing params with strict equality (`===`) against numbers.\n\n```jsx\nfunction PostDetail() {\n  const { postId } = useParams(); \u002F\u002F \"5\" — a string\n\n  \u002F\u002F Bug: 5 === \"5\" is false in JavaScript\n  const post = posts.find(p => p.id === postId); \u002F\u002F undefined!\n\n  \u002F\u002F Fix: coerce before comparing\n  const post2 = posts.find(p => p.id === Number(postId));\n\n  return \u003Cdiv>{post2?.title}\u003C\u002Fdiv>;\n}\n```\n\n**Rule of thumb:** Always coerce params at the top of the component — `Number(id)`, `parseInt(id, 10)`, or validate with Zod — before passing them to any data layer.\n",{"id":1262,"difficulty":96,"q":1263,"a":1264},"multiple-dynamic-segments","Can a single route path contain more than one dynamic segment? Give an example.","Yes. A path can contain **multiple dynamic segments**, each with a unique name. `useParams()` returns all of them in one object.\n\n```jsx\n\u002F\u002F Route definition — two segments\n\u003CRoute path=\"\u002Forgs\u002F:orgId\u002Frepos\u002F:repoId\" element={\u003CRepoDetail \u002F>} \u002F>\n\nfunction RepoDetail() {\n  const { orgId, repoId } = useParams();\n  \u002F\u002F e.g. orgId = \"acme\", repoId = \"dashboard\"\n\n  return (\n    \u003Cp>\n      Org: {orgId} \u002F Repo: {repoId}\n    \u003C\u002Fp>\n  );\n}\n```\n\n**Rule of thumb:** Destructure all params at once at the top of the component; if you need more than three segments, reconsider whether a flatter URL design would be clearer.\n",{"id":1266,"difficulty":121,"q":1267,"a":1268},"splat-routes","What is a splat route in React Router v6 and when would you use one?","A **splat route** (also called a catch-all) uses `*` as the final segment. It matches everything after that point, and the captured string is available as `params[\"*\"]`. It is most often used for **custom 404 pages** or **legacy URL migration**.\n\n```jsx\n\u002F\u002F Catch-all at the root level — renders for any unmatched path\n\u003CRoute path=\"*\" element={\u003CNotFound \u002F>} \u002F>\n\n\u002F\u002F Catch-all inside a layout — only unmatched paths under \u002Fdocs\n\u003CRoute path=\"\u002Fdocs\">\n  \u003CRoute index element={\u003CDocsHome \u002F>} \u002F>\n  \u003CRoute path=\"*\" element={\u003CDocsFallback \u002F>} \u002F>\n\u003C\u002FRoute>\n\nfunction DocsFallback() {\n  const params = useParams();\n  \u002F\u002F params[\"*\"] = \"api\u002Fv2\u002Fmissing-page\"\n  return \u003Cp>Could not find docs page: {params[\"*\"]}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Place the `*` route last among siblings — React Router matches in definition order and will never reach it if an earlier route already matches.\n",{"id":1270,"difficulty":121,"q":1271,"a":1272},"optional-segments","How do you make a route segment optional in React Router v6?","Append `?` to any dynamic segment token to make it **optional**. The param will be `undefined` when the segment is absent.\n\n```jsx\n\u002F\u002F Both \u002Fsearch and \u002Fsearch\u002Fadvanced match this route\n\u003CRoute path=\"\u002Fsearch\u002F:mode?\" element={\u003CSearch \u002F>} \u002F>\n\nfunction Search() {\n  const { mode } = useParams();\n  \u002F\u002F mode is \"advanced\" | undefined\n\n  return (\n    \u003Cdiv>\n      {mode === 'advanced' ? \u003CAdvancedFilters \u002F> : \u003CBasicSearch \u002F>}\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Prefer two explicit routes (`\u002Fsearch` and `\u002Fsearch\u002F:mode`) over an optional segment when the two layouts differ significantly — it keeps each component focused.\n",{"id":1274,"difficulty":96,"q":1275,"a":1276},"nested-routes-outlet","What is Outlet in React Router v6 and why is it needed for nested routes?","**`\u003COutlet \u002F>`** is a placeholder rendered by a parent route component that tells React Router where to inject the matched child route's element. Without it, child routes render nowhere — the URL changes but nothing appears on screen.\n\n```jsx\n\u002F\u002F Route config — Dashboard wraps two child routes\n\u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboardLayout \u002F>}>\n  \u003CRoute index element={\u003COverview \u002F>} \u002F>\n  \u003CRoute path=\"settings\" element={\u003CSettings \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F DashboardLayout.jsx\nimport { Outlet, NavLink } from 'react-router-dom';\n\nfunction DashboardLayout() {\n  return (\n    \u003Cdiv className=\"dashboard\">\n      \u003Cnav>\n        \u003CNavLink to=\"\u002Fdashboard\">Overview\u003C\u002FNavLink>\n        \u003CNavLink to=\"\u002Fdashboard\u002Fsettings\">Settings\u003C\u002FNavLink>\n      \u003C\u002Fnav>\n      {\u002F* child route renders here *\u002F}\n      \u003Cmain>\u003COutlet \u002F>\u003C\u002Fmain>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Every parent route element that has `\u003CRoute>` children must render `\u003COutlet \u002F>`; forgetting it is the most common nested-route bug.\n",{"id":1278,"difficulty":121,"q":1279,"a":1280},"layout-routes","What is a layout route and how does it differ from a regular route?","A **layout route** is a `\u003CRoute>` that has an `element` for shared UI (nav, sidebar, wrappers) but whose `path` is intentionally omitted or set to the parent prefix. It exists purely to provide structure — it never matches by itself; children match the actual URLs.\n\n```jsx\n\u002F\u002F createBrowserRouter style — no path on the layout route\nconst router = createBrowserRouter([\n  {\n    element: \u003CAppShell \u002F>,   \u002F\u002F nav + footer — no \"path\" key\n    children: [\n      { path: '\u002F',          element: \u003CHome \u002F> },\n      { path: '\u002Fabout',     element: \u003CAbout \u002F> },\n      { path: '\u002Fblog\u002F:slug', element: \u003CBlogPost \u002F> },\n    ],\n  },\n]);\n\n\u002F\u002F AppShell renders Outlet — no URL segment consumed\nfunction AppShell() {\n  return (\n    \u003C>\n      \u003CGlobalNav \u002F>\n      \u003COutlet \u002F>\n      \u003CFooter \u002F>\n    \u003C\u002F>\n  );\n}\n```\n\n**Rule of thumb:** Omit `path` on a layout route entirely; adding `path=\"\"` also works but is less readable.\n",{"id":538,"difficulty":96,"q":1282,"a":1283},"What is an index route in React Router v6 and when does it render?","An **index route** is a child route with the `index` prop instead of a `path`. It renders inside the parent's `\u003COutlet \u002F>` when the URL matches the parent's path exactly — acting as the default child.\n\n```jsx\n\u003CRoute path=\"\u002Fteam\" element={\u003CTeamLayout \u002F>}>\n  {\u002F* renders at \u002Fteam exactly *\u002F}\n  \u003CRoute index element={\u003CTeamOverview \u002F>} \u002F>\n  {\u002F* renders at \u002Fteam\u002F:memberId *\u002F}\n  \u003CRoute path=\":memberId\" element={\u003CMemberDetail \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F Without the index route, navigating to \u002Fteam shows\n\u002F\u002F TeamLayout with an empty Outlet — a blank content area.\n```\n\n**Rule of thumb:** Every layout route that owns an `\u003COutlet \u002F>` should have an index route; otherwise the parent path renders a blank content area.\n",{"id":1285,"difficulty":121,"q":1286,"a":1287},"relative-vs-absolute-paths","What is the difference between relative and absolute paths in nested route definitions?","In React Router v6, child `path` values are **relative** by default — they are appended to the parent's path. A **leading `\u002F` makes a path absolute**, bypassing the nesting entirely, which is usually a mistake inside a nested `\u003CRoute>` tree.\n\n```jsx\n\u003CRoute path=\"\u002Fapp\" element={\u003CAppLayout \u002F>}>\n  {\u002F* relative — matches \u002Fapp\u002Fprofile *\u002F}\n  \u003CRoute path=\"profile\" element={\u003CProfile \u002F>} \u002F>\n\n  {\u002F* absolute — matches \u002Fsettings, NOT \u002Fapp\u002Fsettings\n      The parent layout is still rendered because it is\n      an ancestor in the tree, but the URL ignores \u002Fapp *\u002F}\n  \u003CRoute path=\"\u002Fsettings\" element={\u003CSettings \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F Link usage follows the same rule:\n\u002F\u002F \u003CLink to=\"profile\">   — relative, resolves to \u002Fapp\u002Fprofile\n\u002F\u002F \u003CLink to=\"\u002Fprofile\">  — absolute, resolves to \u002Fprofile\n```\n\n**Rule of thumb:** Never start a nested child `path` with `\u002F`; reserve absolute paths for top-level routes only.\n",{"id":1289,"difficulty":121,"q":1290,"a":1291},"deeply-nested-routes","How do you structure three levels of nesting — an app shell, a section layout, and a detail page?","Each level adds one more `\u003CRoute>` wrapper and one more `\u003COutlet \u002F>` in the corresponding component. React Router renders the entire ancestor chain, threading `\u003COutlet \u002F>` down the tree.\n\n```jsx\nconst router = createBrowserRouter([\n  {\n    \u002F\u002F Level 1 — app shell (nav + footer)\n    element: \u003CAppShell \u002F>,\n    children: [\n      {\n        \u002F\u002F Level 2 — section layout (sidebar)\n        path: 'docs',\n        element: \u003CDocsLayout \u002F>,\n        children: [\n          { index: true, element: \u003CDocsHome \u002F> },\n          {\n            \u002F\u002F Level 3 — detail page\n            path: ':slug',\n            element: \u003CDocPage \u002F>,\n          },\n        ],\n      },\n    ],\n  },\n]);\n\n\u002F\u002F AppShell renders \u003COutlet \u002F> → DocsLayout renders \u003COutlet \u002F> → DocPage\n```\n\n**Rule of thumb:** Keep nesting to three levels maximum; deeper trees are hard to reason about and usually signal that the URL design needs flattening.\n",{"id":1293,"difficulty":121,"q":1294,"a":1295},"outlet-context","How do you pass data from a parent route component to its child routes without prop drilling?","Pass a value to **`\u003COutlet context={...} \u002F>`** in the parent and read it with **`useOutletContext()`** in any descendant. This avoids threading props through multiple intermediate components.\n\n```jsx\n\u002F\u002F Parent layout — fetches the user and passes it down\nfunction DashboardLayout() {\n  const user = useCurrentUser(); \u002F\u002F some hook or loader result\n\n  return (\n    \u003Cdiv>\n      \u003CDashboardNav user={user} \u002F>\n      {\u002F* provide user to all child routes *\u002F}\n      \u003COutlet context={{ user }} \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n\n\u002F\u002F Child route — reads the context\nimport { useOutletContext } from 'react-router-dom';\n\nfunction ProfilePage() {\n  const { user } = useOutletContext();\n  return \u003Ch1>Hello, {user.name}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Type the context with a custom hook — `export function useDashboardCtx() { return useOutletContext\u003CDashCtx>(); }` — so TypeScript catches mismatches.\n",{"id":1297,"difficulty":121,"q":1298,"a":1299},"404-inside-nested-layout","How do you show a 404 page that still displays the parent layout's navigation?","Add a **splat child route** (`path=\"*\"`) inside the parent's children. It matches any unrecognised path under the parent and renders inside `\u003COutlet \u002F>`, so the layout's navigation remains visible.\n\n```jsx\n\u003CRoute path=\"\u002Fapp\" element={\u003CAppLayout \u002F>}>\n  \u003CRoute index element={\u003CHome \u002F>} \u002F>\n  \u003CRoute path=\"posts\" element={\u003CPosts \u002F>} \u002F>\n  \u003CRoute path=\"posts\u002F:id\" element={\u003CPostDetail \u002F>} \u002F>\n\n  {\u002F* catches \u002Fapp\u002Fanything-unrecognised *\u002F}\n  \u003CRoute path=\"*\" element={\u003CNotFound \u002F>} \u002F>\n\u003C\u002FRoute>\n\nfunction NotFound() {\n  return (\n    \u003Cdiv>\n      \u003Ch2>404 – Page not found\u003C\u002Fh2>\n      \u003CLink to=\"\u002Fapp\">Back home\u003C\u002FLink>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Add a `path=\"*\"` child to every significant layout; relying on only a root-level catch-all means users lose their navigation context when they land on a 404.\n",{"id":1301,"difficulty":121,"q":1302,"a":1303},"search-params-vs-route-params","What is the difference between route params and URL search params, and which hook reads each?","**Route params** (`:id`) are part of the URL path and identify a resource — they are read with `useParams()`. **Search params** (`?sort=asc&page=2`) are query string key-value pairs used for filtering, sorting, or pagination — they are read and written with `useSearchParams()`.\n\n```jsx\n\u002F\u002F URL: \u002Fproducts\u002F42?color=blue&size=M\n\nfunction ProductDetail() {\n  const { productId } = useParams();       \u002F\u002F \"42\"\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  const color = searchParams.get('color'); \u002F\u002F \"blue\"\n  const size  = searchParams.get('size');  \u002F\u002F \"M\"\n\n  function sortByPrice() {\n    \u002F\u002F updates the query string without a full navigation\n    setSearchParams({ sort: 'price' });\n  }\n\n  return \u003Cp>{productId} — {color} \u002F {size}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Use route params for identity (which resource), search params for state (how to display it) — mixing them leads to bloated URLs and broken back-button behaviour.\n",{"id":1305,"difficulty":121,"q":1306,"a":1307},"create-browser-router-vs-jsx","What are the differences between createBrowserRouter (object config) and the JSX \u003CRoutes>\u002F\u003CRoute> approach?","**`createBrowserRouter`** is the modern Data API introduced in v6.4. It accepts a plain-object route tree and unlocks **loaders**, **actions**, **errorElement**, and **defer** at each route. The older **`\u003CRoutes>\u002F\u003CRoute>` JSX** approach still works but cannot use any Data API features.\n\n```jsx\n\u002F\u002F Object config — enables loaders and actions\nconst router = createBrowserRouter([\n  {\n    path: '\u002Fposts\u002F:id',\n    element: \u003CPostDetail \u002F>,\n    loader: ({ params }) => fetchPost(params.id), \u002F\u002F data API\n    errorElement: \u003CPostError \u002F>,\n  },\n]);\n\u002F\u002F Render: \u003CRouterProvider router={router} \u002F>\n\n\u002F\u002F JSX approach — no loader support\nfunction App() {\n  return (\n    \u003CBrowserRouter>\n      \u003CRoutes>\n        \u003CRoute path=\"\u002Fposts\u002F:id\" element={\u003CPostDetail \u002F>} \u002F>\n      \u003C\u002FRoutes>\n    \u003C\u002FBrowserRouter>\n  );\n}\n```\n\n**Rule of thumb:** Use `createBrowserRouter` for all new projects; it strictly supersedes the JSX approach and is what the React Router team recommends going forward.\n",{"id":1309,"difficulty":208,"q":1310,"a":1311},"loader-pattern","How does a route loader work in React Router v6.4+ and how does the component consume its data?","A **loader** is an async function attached to a route in the object config. React Router calls it before rendering the route, passing `{ params, request }`. The component reads the resolved data via **`useLoaderData()`** — no `useEffect` or loading state needed.\n\n```jsx\n\u002F\u002F Define the loader\nasync function postLoader({ params }) {\n  const res = await fetch(`\u002Fapi\u002Fposts\u002F${params.postId}`);\n  if (!res.ok) throw new Response('Not Found', { status: 404 });\n  return res.json(); \u002F\u002F returned value becomes loader data\n}\n\n\u002F\u002F Attach it to the route\nconst router = createBrowserRouter([\n  {\n    path: '\u002Fposts\u002F:postId',\n    element: \u003CPostDetail \u002F>,\n    loader: postLoader,\n    errorElement: \u003CPostError \u002F>,\n  },\n]);\n\n\u002F\u002F Consume in the component\nimport { useLoaderData } from 'react-router-dom';\n\nfunction PostDetail() {\n  const post = useLoaderData(); \u002F\u002F fully resolved — no loading state\n  return \u003Ch1>{post.title}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Throw a `Response` with an appropriate status code from a loader on errors — React Router will render `errorElement` and `useRouteError()` can read the response status.\n",{"id":1313,"difficulty":208,"q":1314,"a":1315},"error-element-nested","How does errorElement bubble in a nested route tree?","When a loader or renderer throws, React Router walks **up the route tree** looking for the nearest ancestor that declares an **`errorElement`**. The first match renders in place of the failed subtree, preserving all ancestor layouts above it.\n\n```jsx\nconst router = createBrowserRouter([\n  {\n    element: \u003CAppShell \u002F>,        \u002F\u002F no errorElement — bubbles further up\n    children: [\n      {\n        path: 'docs',\n        element: \u003CDocsLayout \u002F>,\n        errorElement: \u003CDocsError \u002F>, \u002F\u002F catches errors in any docs child\n        children: [\n          {\n            path: ':slug',\n            element: \u003CDocPage \u002F>,\n            loader: docLoader,     \u002F\u002F throws 404 → DocsError renders\n          },\n        ],\n      },\n    ],\n  },\n]);\n\nfunction DocsError() {\n  const error = useRouteError(); \u002F\u002F the thrown Response or Error\n  return \u003Cp>Docs error: {error.statusText}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Add an `errorElement` at every meaningful layout boundary so a child failure shows a scoped error UI instead of wiping the whole page.\n",{"id":1317,"difficulty":96,"q":1318,"a":1319},"navigate-programmatic","How do you navigate programmatically to a dynamic route, including the param?","Use the **`useNavigate()`** hook. Call the returned function with the full path string (interpolate params yourself) or with a relative path and a `replace` flag when you want to skip history entries.\n\n```jsx\nimport { useNavigate } from 'react-router-dom';\n\nfunction UserList({ users }) {\n  const navigate = useNavigate();\n\n  function handleSelect(userId) {\n    \u002F\u002F absolute path with the param interpolated\n    navigate(`\u002Fusers\u002F${userId}`);\n\n    \u002F\u002F replace current history entry (e.g. after a form submission)\n    \u002F\u002F navigate(`\u002Fusers\u002F${userId}`, { replace: true });\n  }\n\n  return (\n    \u003Cul>\n      {users.map(u => (\n        \u003Cli key={u.id} onClick={() => handleSelect(u.id)}>\n          {u.name}\n        \u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  );\n}\n```\n\n**Rule of thumb:** Prefer `\u003CLink>` over `useNavigate` for user-initiated navigation — it is more accessible and handles middle-click\u002Fopen-in-new-tab correctly.\n",{"id":1321,"difficulty":96,"q":1322,"a":1323},"active-link-nested","How does NavLink determine its active state inside a nested route?","**`\u003CNavLink>`** compares the `to` prop against the current URL. By default it applies the `active` class when the URL **starts with** the `to` value (prefix match). Set `end` to `true` to require an exact match — essential for parent paths like `\u002Fdashboard`.\n\n```jsx\nfunction DocsNav() {\n  return (\n    \u003Cnav>\n      {\u002F* active for \u002Fdocs AND \u002Fdocs\u002Fanything — usually wrong for the root *\u002F}\n      \u003CNavLink to=\"\u002Fdocs\">Docs Home\u003C\u002FNavLink>\n\n      {\u002F* active only at \u002Fdocs exactly *\u002F}\n      \u003CNavLink to=\"\u002Fdocs\" end>Docs Home\u003C\u002FNavLink>\n\n      {\u002F* active for \u002Fdocs\u002Fapi and \u002Fdocs\u002Fapi\u002F... *\u002F}\n      \u003CNavLink to=\"\u002Fdocs\u002Fapi\">API Reference\u003C\u002FNavLink>\n    \u003C\u002Fnav>\n  );\n}\n```\n\n**Rule of thumb:** Always add `end` to `NavLink` items whose `to` value is an ancestor of other nav items; otherwise the parent link stays highlighted on every child page.\n",{"id":1325,"difficulty":208,"q":1326,"a":1327},"route-param-validation","How would you validate a route param (e.g. ensure :id is numeric) and redirect if it is invalid?","The cleanest place is inside a **loader** — parse and validate the param, then throw a redirect `Response` if it fails. The component never renders with invalid data.\n\n```jsx\nimport { redirect } from 'react-router-dom';\n\nasync function postLoader({ params }) {\n  const id = Number(params.postId);\n\n  \u002F\u002F Redirect to 404 layout if id is not a valid integer\n  if (!Number.isInteger(id) || id \u003C= 0) {\n    throw redirect('\u002F404');           \u002F\u002F or throw new Response('', { status: 404 })\n  }\n\n  const res = await fetch(`\u002Fapi\u002Fposts\u002F${id}`);\n  if (!res.ok) throw new Response('Post not found', { status: 404 });\n  return res.json();\n}\n\nconst router = createBrowserRouter([\n  {\n    path: '\u002Fposts\u002F:postId',\n    loader: postLoader,\n    element: \u003CPostDetail \u002F>,\n    errorElement: \u003CPostError \u002F>,\n  },\n]);\n```\n\n**Rule of thumb:** Validate params in loaders, not in render; by the time the component runs the data should already be known-good.\n",{"id":1329,"difficulty":208,"q":1330,"a":1331},"outlet-context-typescript","How do you type useOutletContext in TypeScript to avoid using any?","Create a **typed wrapper hook** that calls `useOutletContext` with the correct generic. Export it from the layout file so child routes import the typed version, not the raw hook.\n\n```tsx\n\u002F\u002F DashboardLayout.tsx\nimport { Outlet, useOutletContext } from 'react-router-dom';\n\ninterface DashboardCtx {\n  user: { id: number; name: string; role: string };\n  refetch: () => void;\n}\n\n\u002F\u002F Typed wrapper — export this, not useOutletContext directly\nexport function useDashboardCtx() {\n  return useOutletContext\u003CDashboardCtx>();\n}\n\nexport function DashboardLayout() {\n  const user = useCurrentUser();\n  const refetch = useRefetch();\n\n  return (\n    \u003Cdiv>\n      \u003CDashboardNav \u002F>\n      \u003COutlet context={{ user, refetch } satisfies DashboardCtx} \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n\n\u002F\u002F ProfilePage.tsx — fully typed, no any\nimport { useDashboardCtx } from '.\u002FDashboardLayout';\n\nfunction ProfilePage() {\n  const { user, refetch } = useDashboardCtx(); \u002F\u002F typed!\n  return \u003Ch1>{user.name}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Co-locate the typed hook with the layout component that provides the context — it acts as the single source of truth for the context shape.\n",{"description":94},"React Router v6 dynamic and nested routes interview questions — useParams, Outlet, layout routes, index routes, useOutletContext, splat routes, and data loaders.","react\u002Frouting\u002Fdynamic-nested-routes","Dynamic and Nested Routes","JPlp09gxgi2ITBy45JiCXT1aq106IlC4O1y4c0dPcbM",{"id":1338,"title":1339,"body":1340,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1344,"navigation":100,"order":11,"path":1346,"questions":1347,"questionsCount":493,"related":169,"seo":1408,"seoDescription":1409,"stem":1410,"subtopic":1411,"topic":36,"topicSlug":38,"updated":420,"__hash__":1412},"qa\u002Freact\u002Fstate-and-data-flow\u002Fcontext-api.md","Context Api",{"type":91,"value":1341,"toc":1342},[],{"title":94,"searchDepth":11,"depth":11,"links":1343},[],{"subtopicSlug":1345},"context-api","\u002Freact\u002Fstate-and-data-flow\u002Fcontext-api",[1348,1352,1356,1360,1364,1368,1372,1376,1380,1384,1388,1392,1396,1400,1404],{"id":1349,"difficulty":96,"q":1350,"a":1351},"context-what","What problem does the React Context API solve?","Context provides a way to pass data through the component tree **without\nthreading props through every intermediate level** — solving what is\nknown as **prop drilling**.\n\n```jsx\n\u002F\u002F Without Context — every layer must pass `theme` down\n\u003CApp theme=\"dark\">\n  \u003CLayout theme=\"dark\">\n    \u003CSidebar theme=\"dark\">\n      \u003CButton theme=\"dark\" \u002F>\n    \u003C\u002FSidebar>\n  \u003C\u002FLayout>\n\u003C\u002FApp>\n\n\u002F\u002F With Context — Button reads theme directly\nconst ThemeContext = createContext('light')\n\nfunction App() {\n  return (\n    \u003CThemeContext.Provider value=\"dark\">\n      \u003CLayout \u002F>         {\u002F* no theme prop needed *\u002F}\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nfunction Button() {\n  const theme = useContext(ThemeContext)   \u002F\u002F reads directly\n  return \u003Cbutton className={theme}>Click\u003C\u002Fbutton>\n}\n```\n\n**Rule of thumb:** Use Context for truly global or widely shared data\n(theme, locale, auth user). Don't reach for it to avoid one or two\nlevels of prop passing.\n",{"id":1353,"difficulty":96,"q":1354,"a":1355},"context-create","What does createContext do and what does its argument represent?","`createContext(defaultValue)` creates a Context object. The\n**default value** is used only when a component reads the context\n*without* a matching `Provider` above it in the tree — it is not the\ninitial value of the Provider.\n\n```jsx\n\u002F\u002F Default value is 'light' — used only outside any Provider\nconst ThemeContext = createContext('light')\n\nfunction App() {\n  return (\n    \u003CThemeContext.Provider value=\"dark\">\n      \u003CChild \u002F>   {\u002F* reads 'dark' *\u002F}\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nfunction Orphan() {\n  \u002F\u002F No Provider above → reads the default 'light'\n  const theme = useContext(ThemeContext)\n  return \u003Cdiv>{theme}\u003C\u002Fdiv>\n}\n```\n\n**Rule of thumb:** Set the default value to something that makes the\ncomponent usable in isolation (e.g. in tests or Storybook) without\nwrapping it in a Provider.\n",{"id":1357,"difficulty":96,"q":1358,"a":1359},"context-use-context","How do you consume a context value inside a function component?","Call `useContext(MyContext)` at the top level of the component. It\nreturns the current context value provided by the nearest matching\n`Provider` up the tree.\n\n```jsx\nimport { createContext, useContext } from 'react'\n\nconst UserContext = createContext(null)\n\nfunction Greeting() {\n  const user = useContext(UserContext)\n  if (!user) return \u003Cp>Please log in\u003C\u002Fp>\n  return \u003Cp>Hello, {user.name}\u003C\u002Fp>\n}\n\nfunction App() {\n  const [user] = useState({ name: 'Alice' })\n  return (\n    \u003CUserContext.Provider value={user}>\n      \u003CGreeting \u002F>\n    \u003C\u002FUserContext.Provider>\n  )\n}\n```\n\n**Rule of thumb:** `useContext` is the modern replacement for the legacy\n`Context.Consumer` render-prop API. Use it exclusively in function\ncomponents.\n",{"id":1361,"difficulty":121,"q":1362,"a":1363},"context-re-renders","When does a context change cause re-renders, and which components are affected?","Every component that calls `useContext(MyContext)` **re-renders** when\nthe Provider's `value` prop changes (by reference). Intermediate\ncomponents that don't consume the context are **not** re-rendered.\n\n```jsx\nconst CountCtx = createContext(0)\n\nfunction Parent() {\n  const [count, setCount] = useState(0)\n  return (\n    \u002F\u002F Object literal creates a new reference every render — all consumers re-render\n    \u003CCountCtx.Provider value={{ count, setCount }}>\n      \u003CMiddle \u002F>\n    \u003C\u002FCountCtx.Provider>\n  )\n}\n\nfunction Middle() {\n  \u002F\u002F does NOT use CountCtx → NOT re-rendered when count changes\n  return \u003CConsumer \u002F>\n}\n\nfunction Consumer() {\n  const { count } = useContext(CountCtx)   \u002F\u002F re-renders on every count change\n  return \u003Cspan>{count}\u003C\u002Fspan>\n}\n```\n\n**Rule of thumb:** Memoize the context value (`useMemo`) when the\nProvider's parent re-renders frequently to avoid creating a new\nreference each time.\n",{"id":1365,"difficulty":121,"q":1366,"a":1367},"context-value-memo","Why should you memoize the value passed to a Context Provider?","Every time the Provider's parent re-renders, a new object or array\nliteral creates a **new reference**. React's shallow equality check\nsees a new reference → all consumers re-render even if the data\nhasn't changed.\n\n```jsx\n\u002F\u002F ❌ New object every render → all consumers re-render needlessly\n\u003CAuthContext.Provider value={{ user, logout }}>\n\n\u002F\u002F ✅ Stable reference — only changes when user or logout changes\nconst authValue = useMemo(() => ({ user, logout }), [user, logout])\n\u003CAuthContext.Provider value={authValue}>\n```\n\nIf the value is a primitive (string, number) memoisation is\nunnecessary because primitives are compared by value.\n\n**Rule of thumb:** Whenever you pass an object or array as a context\nvalue, wrap it in `useMemo`.\n",{"id":1369,"difficulty":121,"q":1370,"a":1371},"context-multiple","Can a component use multiple contexts, and how do you compose multiple Providers?","Yes. Call `useContext` once per context. Compose Providers by nesting\nthem at the top of the tree — order only matters if a Provider reads\nfrom a sibling context.\n\n```jsx\nfunction App() {\n  return (\n    \u003CThemeContext.Provider value=\"dark\">\n      \u003CAuthContext.Provider value={currentUser}>\n        \u003CLocaleContext.Provider value=\"en\">\n          \u003CRouter \u002F>\n        \u003C\u002FLocaleContext.Provider>\n      \u003C\u002FAuthContext.Provider>\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nfunction Header() {\n  const theme  = useContext(ThemeContext)\n  const user   = useContext(AuthContext)\n  const locale = useContext(LocaleContext)\n  \u002F\u002F ...\n}\n```\n\nIf nesting becomes unwieldy, extract a single `AppProviders` wrapper\ncomponent that composes them all.\n\n**Rule of thumb:** Create one context per concern; don't cram unrelated\nvalues into a single context object.\n",{"id":1373,"difficulty":121,"q":1374,"a":1375},"context-vs-props","When should you use Context instead of props?","Use Context when data is needed by **many components at different nesting\nlevels** and passing it via props would require threading it through\nmany intermediate layers that don't need it themselves.\n\n| Signal | Prefer |\n|---|---|\n| 1–2 levels of passing | Props |\n| Truly global (theme, locale, auth) | Context |\n| Frequently changes, many subscribers | External store |\n\n```jsx\n\u002F\u002F Props are fine — shallow tree, one consumer\n\u003CPage>\u003CCard title={title} \u002F>\u003C\u002FPage>\n\n\u002F\u002F Context fits — auth needed everywhere, changes rarely\n\u003CAuthContext.Provider value={user}>\n  \u003CNavbar \u002F>\u003CRoutes \u002F>\u003CFooter \u002F>\n\u003C\u002FAuthContext.Provider>\n```\n\n**Rule of thumb:** If you're passing the same prop through 3+ levels\nthat don't use it, that's the signal to consider Context.\n",{"id":1377,"difficulty":121,"q":1378,"a":1379},"context-vs-redux","What are the main differences between Context and a library like Redux?","| Aspect | Context | Redux \u002F Zustand |\n|---|---|---|\n| Built-in | Yes | External dependency |\n| Re-render granularity | All consumers of the context | Selector-level (only subscribe to what you need) |\n| DevTools | None | Time-travel, action log |\n| Async logic | Manual | Built-in middleware (thunk, saga) |\n| Boilerplate | Low | Medium (RTK reduces it) |\n\nContext is best for **infrequently changing** global values (theme,\nlocale, authenticated user). For **frequently updating** shared state\nwith many subscribers, a store library avoids cascade re-renders.\n\n**Rule of thumb:** Don't reach for Redux just because you have global\nstate. Context + `useReducer` covers a lot of ground; add a store\nwhen you need fine-grained subscriptions or middleware.\n",{"id":1381,"difficulty":121,"q":1382,"a":1383},"context-custom-hook","Why is it a good practice to wrap useContext in a custom hook?","A custom hook hides the context import, adds a helpful error message\nwhen used outside the Provider, and creates a stable API so consumers\ndon't need to know which context backs it.\n\n```jsx\n\u002F\u002F context\u002FThemeContext.jsx\nconst ThemeContext = createContext(null)\n\nexport function ThemeProvider({ children }) {\n  const [theme, setTheme] = useState('dark')\n  return (\n    \u003CThemeContext.Provider value={{ theme, setTheme }}>\n      {children}\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nexport function useTheme() {\n  const ctx = useContext(ThemeContext)\n  if (!ctx) throw new Error('useTheme must be used inside ThemeProvider')\n  return ctx\n}\n\n\u002F\u002F Consumer — no context import needed\nconst { theme, setTheme } = useTheme()\n```\n\n**Rule of thumb:** Always export a `useFoo()` hook instead of\nexporting the raw Context object.\n",{"id":1385,"difficulty":208,"q":1386,"a":1387},"context-split-concerns","How can you split a context to avoid unnecessary re-renders when only part of the value changes?","Provide **separate contexts** for state and dispatch (or read vs.\nwrite). Components that only dispatch won't re-render when state\nchanges, and vice versa.\n\n```jsx\nconst CountStateCtx   = createContext(null)\nconst CountDispatchCtx = createContext(null)\n\nfunction CountProvider({ children }) {\n  const [count, dispatch] = useReducer(reducer, 0)\n  return (\n    \u003CCountDispatchCtx.Provider value={dispatch}>\n      \u003CCountStateCtx.Provider value={count}>\n        {children}\n      \u003C\u002FCountStateCtx.Provider>\n    \u003C\u002FCountDispatchCtx.Provider>\n  )\n}\n\n\u002F\u002F Only re-renders when count changes\nfunction Counter()   { return \u003Cspan>{useContext(CountStateCtx)}\u003C\u002Fspan> }\n\n\u002F\u002F Never re-renders due to count changes (dispatch is stable)\nfunction IncrBtn()   { return \u003Cbutton onClick={() => useContext(CountDispatchCtx)({ type: 'inc' })}>+\u003C\u002Fbutton> }\n```\n\n**Rule of thumb:** If your context value has both frequently-changing\nstate and stable callbacks, put them in separate contexts.\n",{"id":1389,"difficulty":121,"q":1390,"a":1391},"context-with-reducer","How do you combine useReducer with Context to implement a lightweight global store?","Keep `useReducer` in a Provider component. Pass state via one context\nand the stable `dispatch` via another (or together if updates are\ninfrequent).\n\n```jsx\nconst StoreCtx = createContext(null)\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'SET_USER': return { ...state, user: action.payload }\n    case 'LOGOUT':   return { ...state, user: null }\n    default:         return state\n  }\n}\n\nexport function StoreProvider({ children }) {\n  const [state, dispatch] = useReducer(reducer, { user: null })\n  const value = useMemo(() => ({ state, dispatch }), [state])\n  return \u003CStoreCtx.Provider value={value}>{children}\u003C\u002FStoreCtx.Provider>\n}\n\nexport const useStore = () => useContext(StoreCtx)\n```\n\nThis pattern gives you Redux-like action dispatch without adding a\ndependency — ideal for small to medium apps.\n\n**Rule of thumb:** `useReducer` + Context is the sweet spot before you\nneed Redux's devtools or middleware.\n",{"id":1393,"difficulty":208,"q":1394,"a":1395},"context-lazy-init","How do you avoid expensive context value recomputation on every render?","Use `useMemo` for objects\u002Farrays passed as the context value, and\n`useCallback` for functions, so React sees a stable reference and\nskips re-rendering consumers.\n\n```jsx\nfunction AuthProvider({ children }) {\n  const [user, setUser] = useState(null)\n\n  const logout = useCallback(() => {\n    setUser(null)\n    api.logout()\n  }, [])                         \u002F\u002F stable function reference\n\n  const value = useMemo(\n    () => ({ user, logout }),\n    [user, logout]               \u002F\u002F recompute only when user changes\n  )\n\n  return \u003CAuthContext.Provider value={value}>{children}\u003C\u002FAuthContext.Provider>\n}\n```\n\n**Rule of thumb:** Any context value that is an object or contains\nfunctions should be memoised at the Provider level.\n",{"id":1397,"difficulty":121,"q":1398,"a":1399},"context-default-null","Should the default value of createContext be null or a sensible fallback, and why?","Using `null` (or `undefined`) as the default forces consumers to handle\nthe \"no Provider\" case explicitly and makes it easy to detect misuse.\nA non-null default value makes the component work outside a Provider,\nwhich is useful for testing and Storybook.\n\n```jsx\n\u002F\u002F null default — consumers must check or use a custom hook with an error\nconst AuthContext = createContext(null)\n\n\u002F\u002F Non-null default — component works in isolation; good for theming\nconst ThemeContext = createContext({ mode: 'light', toggle: () => {} })\n```\n\nChoose based on intent:\n- If the component **cannot** work without a Provider → default `null`,\n  throw in the custom hook.\n- If the component **can** work standalone → provide a real fallback.\n\n**Rule of thumb:** Auth\u002Fsession contexts default to `null`; config\u002Ftheme\ncontexts default to a sensible fallback object.\n",{"id":1401,"difficulty":96,"q":1402,"a":1403},"context-provider-location","Where in the component tree should a Context Provider be placed?","As **low as possible** while still wrapping all consumers. Placing it\ntoo high (e.g. at app root) means unrelated subtrees re-render when\nthe context value changes.\n\n```jsx\n\u002F\u002F ❌ Too high — the entire app re-renders when modal state changes\nfunction App() {\n  const [isOpen, setIsOpen] = useState(false)\n  return (\n    \u003CModalContext.Provider value={{ isOpen, setIsOpen }}>\n      \u003CHeader \u002F>\u003CMain \u002F>\u003CFooter \u002F>\n    \u003C\u002FModalContext.Provider>\n  )\n}\n\n\u002F\u002F ✅ Scoped to the subtree that needs it\nfunction CheckoutPage() {\n  const [isOpen, setIsOpen] = useState(false)\n  return (\n    \u003CModalContext.Provider value={{ isOpen, setIsOpen }}>\n      \u003CCheckoutForm \u002F>\u003CConfirmModal \u002F>\n    \u003C\u002FModalContext.Provider>\n  )\n}\n```\n\n**Rule of thumb:** The Provider should be the lowest ancestor that\ncontains all the components that consume the context.\n",{"id":1405,"difficulty":121,"q":1406,"a":1407},"context-testing","How do you test a component that depends on a Context value?","Wrap the component under test in the Provider and supply a controlled\ntest value. With React Testing Library:\n\n```jsx\nimport { render, screen } from '@testing-library\u002Freact'\nimport { ThemeContext } from '.\u002FThemeContext'\nimport ThemedButton from '.\u002FThemedButton'\n\nfunction renderWithTheme(ui, theme = 'light') {\n  return render(\n    \u003CThemeContext.Provider value={theme}>\n      {ui}\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\ntest('applies dark class in dark mode', () => {\n  renderWithTheme(\u003CThemedButton \u002F>, 'dark')\n  expect(screen.getByRole('button')).toHaveClass('dark')\n})\n```\n\nAlternatively, if you use the custom-hook pattern, wrap in the real\nProvider component from your codebase.\n\n**Rule of thumb:** Never mock context directly — always wrap with the\nreal Provider so tests exercise the full integration.\n",{"description":94},"React Context API interview questions — createContext, useContext, Provider, re-render behaviour, multiple contexts, and when to use Context vs. Redux.","react\u002Fstate-and-data-flow\u002Fcontext-api","Context API","p6lfuFzLj8fO3W-l3h9Fm_ohOc8gnQ0e80jy7K7bRmU",{"id":1414,"title":1415,"body":1416,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1420,"navigation":100,"order":11,"path":1421,"questions":1422,"questionsCount":1495,"related":169,"seo":1496,"seoDescription":1497,"stem":1498,"subtopic":1415,"topic":63,"topicSlug":65,"updated":420,"__hash__":1499},"qa\u002Freact\u002Fstate-management\u002Fzustand.md","Zustand",{"type":91,"value":1417,"toc":1418},[],{"title":94,"searchDepth":11,"depth":11,"links":1419},[],{},"\u002Freact\u002Fstate-management\u002Fzustand",[1423,1427,1431,1435,1439,1443,1447,1451,1455,1459,1463,1467,1471,1475,1479,1483,1487,1491],{"id":1424,"difficulty":96,"q":1425,"a":1426},"what-is-zustand","What is Zustand and what problem does it solve?","**Zustand** (German for \"state\") is a lightweight React state-management\nlibrary built on a **flux-inspired one-way data flow** but with almost\nzero boilerplate. It stores state outside the React tree in a plain\nJavaScript object and lets any component subscribe to it without\nrequiring a Provider wrapper.\n\nThe problem it solves: Context re-renders every consumer when any part\nof the context value changes, and Redux forces you to write reducers,\naction types, action creators, and selectors just to add a counter.\nZustand replaces all of that with a single `create()` call.\n\n```js\nimport { create } from 'zustand'\n\nconst useCounterStore = create((set) => ({\n  count: 0,                          \u002F\u002F state\n  increment: () => set((s) => ({ count: s.count + 1 })), \u002F\u002F action\n  reset:     () => set({ count: 0 }),\n}))\n\n\u002F\u002F Consume in any component — no Provider needed\nfunction Counter() {\n  const count     = useCounterStore((s) => s.count)\n  const increment = useCounterStore((s) => s.increment)\n  return \u003Cbutton onClick={increment}>{count}\u003C\u002Fbutton>\n}\n```\n\nThe store is a React hook, state is mutable through `set`, and\ncomponents only re-render when the slice they selected changes.\n\n**Rule of thumb:** Reach for Zustand when Context performance starts\nhurting or when you need global state that isn't tied to a single\ncomponent subtree.\n",{"id":1428,"difficulty":96,"q":1429,"a":1430},"create-store","How do you create a Zustand store with `create()`?","`create()` accepts a **callback** that receives `set` (and optionally\n`get`) and returns the initial state object. State properties and\n**actions** (functions that update state) live in the same object — there\nare no separate reducers or action types.\n\n```js\nimport { create } from 'zustand'\n\nconst useBearStore = create((set, get) => ({\n  bears: 0,\n  honey: 100,\n\n  \u002F\u002F Action using updater function — safe when next value depends on current\n  addBear: () => set((state) => ({ bears: state.bears + 1 })),\n\n  \u002F\u002F Action using partial object — fine when value is independent\n  removeAllBears: () => set({ bears: 0 }),\n\n  \u002F\u002F Action reading current state via get()\n  eatHoney: (amount) => {\n    const { honey } = get()          \u002F\u002F read without subscribing\n    if (honey >= amount) set({ honey: honey - amount })\n  },\n}))\n```\n\n`set` does a **shallow merge** (like `setState` in class components),\nso you only specify the keys you want to update. Pass `true` as the\nsecond argument (`set(fn, true)`) to replace rather than merge.\n\n**Rule of thumb:** Put both state fields and their mutation functions\nin one `create()` call — it keeps related data and logic together and\navoids cross-file action imports.\n",{"id":1432,"difficulty":96,"q":1433,"a":1434},"selectors","Why should you use a selector when reading from a Zustand store?","Without a selector the component re-renders on **every store update**,\neven changes unrelated to what it displays. A **selector** is a function\npassed to the store hook that picks only the slice the component needs;\nZustand compares the return value between renders and skips the re-render\nif it hasn't changed.\n\n```js\nconst useBearStore = create(() => ({ bears: 0, honey: 100 }))\n\n\u002F\u002F ❌ No selector — subscribes to the whole store object.\n\u002F\u002F    Re-renders whenever bears OR honey changes.\nfunction BearsDisplay() {\n  const state = useBearStore()   \u002F\u002F returns { bears, honey }\n  return \u003Cspan>{state.bears}\u003C\u002Fspan>\n}\n\n\u002F\u002F ✅ Selector — subscribes only to bears.\n\u002F\u002F    Re-renders only when bears changes.\nfunction BearsDisplay() {\n  const bears = useBearStore((s) => s.bears)\n  return \u003Cspan>{bears}\u003C\u002Fspan>\n}\n```\n\nZustand uses `Object.is` equality by default. For selectors that return\nobjects or arrays, swap in `shallow` from `zustand\u002Fshallow` so that\na new object with the same keys doesn't trigger a re-render.\n\n**Rule of thumb:** Always select the smallest slice you need, just as\nyou would with a Redux `useSelector`.\n",{"id":1436,"difficulty":96,"q":1437,"a":1438},"actions-inside-store","Should Zustand actions live inside or outside the store?","The **idiomatic Zustand approach** is to define actions **inside** the\n`create()` callback alongside the state they mutate. This co-location\nmeans actions can call `set` and `get` directly without being passed as\narguments, and consumers import a single hook instead of separate\naction creators.\n\n```js\nconst useUserStore = create((set, get) => ({\n  user: null,\n  isLoading: false,\n\n  \u002F\u002F Action defined inside — has closure access to set and get\n  setUser: (user) => set({ user }),\n\n  clearUser: () => set({ user: null }),\n\n  \u002F\u002F Read other state fields via get() inside an action\n  isAdmin: () => get().user?.role === 'admin',\n}))\n\n\u002F\u002F Consumers just call the action directly\nfunction LoginButton() {\n  const setUser = useUserStore((s) => s.setUser)\n  return \u003Cbutton onClick={() => setUser({ id: 1, role: 'admin' })}>\n    Login\n  \u003C\u002Fbutton>\n}\n```\n\nThe alternative — defining actions outside via `useUserStore.setState` —\nworks but scatters logic and is usually only worthwhile in very large\nstores where the slices pattern is used.\n\n**Rule of thumb:** Keep actions inside `create()` unless your store\ngrows so large that the slices pattern becomes necessary.\n",{"id":1440,"difficulty":121,"q":1441,"a":1442},"async-actions","How do you handle async actions in Zustand?","Zustand requires **no special middleware** for async work. Because actions\nare plain functions, you can use `async\u002Fawait` directly. Call `set`\nonce or multiple times inside the async function to reflect loading,\nsuccess, and error states.\n\n```js\nconst usePostsStore = create((set) => ({\n  posts: [],\n  loading: false,\n  error: null,\n\n  fetchPosts: async () => {\n    set({ loading: true, error: null })        \u002F\u002F show spinner\n\n    try {\n      const res  = await fetch('\u002Fapi\u002Fposts')\n      const data = await res.json()\n      set({ posts: data, loading: false })     \u002F\u002F success\n    } catch (err) {\n      set({ error: err.message, loading: false }) \u002F\u002F failure\n    }\n  },\n}))\n\nfunction PostList() {\n  const { posts, loading, fetchPosts } = usePostsStore()\n  \u002F\u002F fetchPosts is a stable reference — safe in useEffect deps\n  useEffect(() => { fetchPosts() }, [fetchPosts])\n  if (loading) return \u003Cp>Loading…\u003C\u002Fp>\n  return \u003Cul>{posts.map(p => \u003Cli key={p.id}>{p.title}\u003C\u002Fli>)}\u003C\u002Ful>\n}\n```\n\nThis is one of Zustand's biggest DX wins over Redux, where you need\n`redux-thunk` or `redux-saga` to do the same thing.\n\n**Rule of thumb:** Write async actions exactly like any `async` function\n— no middleware, no special patterns, just `await` and `set`.\n",{"id":1444,"difficulty":121,"q":1445,"a":1446},"shallow-equality","When and why do you use `shallow` equality in Zustand?","Zustand's default comparison is `Object.is` — fine for primitives and\nstable references, but it fails when a selector returns a **new object\nor array on every call**. Import `shallow` from `zustand\u002Fshallow` and\npass it as the second argument to suppress spurious re-renders.\n\n```js\nimport { shallow } from 'zustand\u002Fshallow'\n\nconst useStore = create(() => ({\n  name: 'Alice',\n  age: 30,\n  theme: 'dark',\n}))\n\n\u002F\u002F ❌ Without shallow — new object every render → always re-renders\nfunction Profile() {\n  const { name, age } = useStore((s) => ({ name: s.name, age: s.age }))\n  return \u003Cp>{name}, {age}\u003C\u002Fp>\n}\n\n\u002F\u002F ✅ With shallow — compares keys one level deep → re-renders only\n\u002F\u002F    when name or age actually changes\nfunction Profile() {\n  const { name, age } = useStore(\n    (s) => ({ name: s.name, age: s.age }),\n    shallow,               \u002F\u002F second argument\n  )\n  return \u003Cp>{name}, {age}\u003C\u002Fp>\n}\n\n\u002F\u002F Also works for picking multiple fields as an array\nconst [name, age] = useStore((s) => [s.name, s.age], shallow)\n```\n\n**Rule of thumb:** Use `shallow` whenever your selector returns an\nobject literal or array; skip it when the selector returns a single\nprimitive or stable reference.\n",{"id":1448,"difficulty":121,"q":1449,"a":1450},"devtools-middleware","How do you wire up Redux DevTools with Zustand?","Wrap the `create()` callback with the **`devtools`** middleware from\n`zustand\u002Fmiddleware`. Once connected, every `set` call appears as a\nnamed action in the Redux DevTools extension, and you get time-travel\ndebugging for free.\n\n```js\nimport { create }   from 'zustand'\nimport { devtools } from 'zustand\u002Fmiddleware'\n\nconst useCounterStore = create(\n  devtools(\n    (set) => ({\n      count: 0,\n      \u002F\u002F Name each action for the DevTools action log\n      increment: () => set(\n        (s) => ({ count: s.count + 1 }),\n        false,                    \u002F\u002F don't replace — merge\n        'counter\u002Fincrement',      \u002F\u002F action name shown in DevTools\n      ),\n      reset: () => set({ count: 0 }, false, 'counter\u002Freset'),\n    }),\n    { name: 'CounterStore' },     \u002F\u002F store name shown in DevTools\n  ),\n)\n```\n\nYou can pass `{ enabled: process.env.NODE_ENV === 'development' }` to\nthe `devtools` options to strip it from production bundles.\n\n**Rule of thumb:** Add `devtools` early in development — the action\nnaming discipline pays dividends when debugging complex state flows.\n",{"id":1452,"difficulty":121,"q":1453,"a":1454},"persist-middleware","How does the `persist` middleware work in Zustand?","The **`persist`** middleware from `zustand\u002Fmiddleware` serializes store\nstate to a storage engine (localStorage by default) after every `set`\nand rehydrates it on page load. You wrap your store definition exactly\nlike `devtools`.\n\n```js\nimport { create }  from 'zustand'\nimport { persist, createJSONStorage } from 'zustand\u002Fmiddleware'\n\nconst useSettingsStore = create(\n  persist(\n    (set) => ({\n      theme: 'dark',\n      fontSize: 16,\n      setTheme:    (t) => set({ theme: t }),\n      setFontSize: (s) => set({ fontSize: s }),\n    }),\n    {\n      name:    'user-settings',          \u002F\u002F localStorage key\n      storage: createJSONStorage(() => localStorage), \u002F\u002F default\n      \u002F\u002F Persist only a subset of state\n      partialize: (state) => ({ theme: state.theme }),\n    },\n  ),\n)\n```\n\nFor SSR \u002F Next.js, swap to `sessionStorage` or a custom storage\nadapter so hydration mismatches are avoided. `partialize` lets you\nexclude sensitive or derived fields (like loading flags) from the\npersisted snapshot.\n\n**Rule of thumb:** Always use `partialize` to be explicit about which\nfields survive a page reload — persisting loading or error flags causes\nstale UI on startup.\n",{"id":1456,"difficulty":121,"q":1457,"a":1458},"immer-middleware","How does Zustand's `immer` middleware change how you write state updates?","By default, `set` in Zustand requires you to return a **new partial\nobject**. Wrapping with the **`immer`** middleware lets you write\nmutations directly on a draft — Immer produces the new immutable state\nbehind the scenes.\n\n```js\nimport { create }  from 'zustand'\nimport { immer  }  from 'zustand\u002Fmiddleware\u002Fimmer'\n\nconst useCartStore = create(\n  immer((set) => ({\n    items: [],\n\n    \u002F\u002F Without immer you'd write: set(s => ({ items: [...s.items, item] }))\n    addItem: (item) => set((state) => {\n      state.items.push(item)           \u002F\u002F direct mutation — Immer handles it\n    }),\n\n    removeItem: (id) => set((state) => {\n      state.items = state.items.filter(i => i.id !== id)\n    }),\n\n    updateQty: (id, qty) => set((state) => {\n      const item = state.items.find(i => i.id === id)\n      if (item) item.qty = qty         \u002F\u002F nested mutation — safe with Immer\n    }),\n  })),\n)\n```\n\nWithout `immer`, updating nested objects requires spreading every layer\nmanually. With it you write imperative mutations and Immer converts them\nto structural shares.\n\n**Rule of thumb:** Add `immer` when your state has deeply nested\nstructures; skip it for flat state where spread syntax is readable\nenough.\n",{"id":1460,"difficulty":121,"q":1461,"a":1462},"slices-pattern","What is the slices pattern in Zustand and when should you use it?","As a store grows, cramming all state and actions into one `create()` call\nbecomes unwieldy. The **slices pattern** splits the store into separate\nfunctions (slices), each responsible for a domain, and merges them in a\nsingle `create()` call.\n\n```js\n\u002F\u002F slices\u002FauthSlice.js\nexport const createAuthSlice = (set) => ({\n  user: null,\n  login:  (u) => set({ user: u }),\n  logout: ()  => set({ user: null }),\n})\n\n\u002F\u002F slices\u002FcartSlice.js\nexport const createCartSlice = (set) => ({\n  items: [],\n  addItem:    (item) => set((s) => ({ items: [...s.items, item] })),\n  clearCart:  ()     => set({ items: [] }),\n})\n\n\u002F\u002F store.js — combine slices into one store\nimport { create }          from 'zustand'\nimport { createAuthSlice } from '.\u002Fslices\u002FauthSlice'\nimport { createCartSlice } from '.\u002Fslices\u002FcartSlice'\n\nexport const useStore = create((...args) => ({\n  ...createAuthSlice(...args),\n  ...createCartSlice(...args),\n}))\n\n\u002F\u002F Consumers select from the unified store as usual\nconst user  = useStore((s) => s.user)\nconst items = useStore((s) => s.items)\n```\n\nThis is the Zustand-recommended approach for large stores. Each slice\nfile stays focused and testable independently.\n\n**Rule of thumb:** Introduce slices once your store exceeds ~5 concerns\nor the single file becomes hard to navigate.\n",{"id":1464,"difficulty":121,"q":1465,"a":1466},"subscribe-outside-react","How do you subscribe to a Zustand store outside of a React component?","The store object created by `create()` is also a vanilla store with a\n`.subscribe()` method. You can call it in plain JS modules, Node.js\nscripts, or outside the component tree — no hooks needed.\n\n```js\nconst useStore = create((set) => ({\n  count: 0,\n  increment: () => set((s) => ({ count: s.count + 1 })),\n}))\n\n\u002F\u002F Subscribe outside React\nconst unsub = useStore.subscribe(\n  (state) => state.count,   \u002F\u002F selector — called only when count changes\n  (count, prevCount) => {\n    console.log(`count changed: ${prevCount} → ${count}`)\n    if (count >= 10) analytics.track('milestone_10')\n  },\n)\n\n\u002F\u002F Read state without subscribing\nconst current = useStore.getState().count\n\n\u002F\u002F Set state without a component\nuseStore.setState({ count: 0 })\n\n\u002F\u002F Clean up when done\nunsub()\n```\n\nThis is useful for analytics side effects, syncing with non-React\nsystems, or writing integration tests that assert state without\nrendering.\n\n**Rule of thumb:** Use `.subscribe()` \u002F `.getState()` \u002F `.setState()`\nfor interactions with the store that live outside the React lifecycle.\n",{"id":1468,"difficulty":208,"q":1469,"a":1470},"zustand-vs-redux-toolkit","What are the trade-offs between Zustand and Redux Toolkit?","Both libraries manage global state in a React app, but they occupy\ndifferent points on the complexity\u002Fpower spectrum.\n\n| Concern | Zustand | Redux Toolkit |\n|---|---|---|\n| Bundle size | ~1 kB | ~12 kB |\n| Boilerplate | Minimal (`create` + actions) | Slices + configure + reducers |\n| Devtools | Yes (via middleware) | First-class, built-in |\n| Async | Plain async functions | `createAsyncThunk` \u002F RTK Query |\n| Data fetching | DIY or Zustand + SWR\u002FTanStack | RTK Query (cache, dedup) |\n| Middleware ecosystem | Small (devtools, persist, immer) | Large (mature) |\n| TypeScript | Good | Excellent |\n| Learning curve | Low | Medium-High |\n\n```js\n\u002F\u002F Same counter — Zustand vs RTK\n\n\u002F\u002F Zustand (6 lines)\nconst useCounter = create((set) => ({\n  value: 0,\n  increment: () => set((s) => ({ value: s.value + 1 })),\n}))\n\n\u002F\u002F Redux Toolkit (25+ lines)\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: { increment: (state) => { state.value++ } },\n})\nconst store = configureStore({ reducer: { counter: counterSlice.reducer } })\n```\n\nChoose Redux Toolkit when: you have a large team that benefits from\nstrict conventions, you need RTK Query for server-cache management, or\nyou're already invested in the Redux ecosystem. Choose Zustand when: you\nwant quick setup, smaller bundle, or a library that stays out of your way.\n\n**Rule of thumb:** Zustand for small-to-medium apps; Redux Toolkit when\nthe app is large, team is big, or RTK Query's server-state caching is\nworth the extra weight.\n",{"id":1472,"difficulty":208,"q":1473,"a":1474},"zustand-vs-context","How does Zustand compare to the React Context API for global state?","Context and Zustand solve the same problem — sharing state across the\ntree — but with very different performance models.\n\n```jsx\n\u002F\u002F Context: every consumer re-renders when ANY part of value changes\nconst AppCtx = createContext(null)\nfunction AppProvider({ children }) {\n  const [count, setCount] = useState(0)\n  const [user,  setUser ] = useState(null)\n  \u002F\u002F ❌ Changing count re-renders components that only read user\n  return \u003CAppCtx.Provider value={{ count, setCount, user, setUser }}>\n    {children}\n  \u003C\u002FAppCtx.Provider>\n}\n\n\u002F\u002F Zustand: each component subscribes only to the slice it selects\nconst useAppStore = create((set) => ({\n  count: 0, setCount: (n) => set({ count: n }),\n  user:  null, setUser: (u) => set({ user: u }),\n}))\n\n\u002F\u002F This component NEVER re-renders when count changes\nfunction UserMenu() {\n  const user = useAppStore((s) => s.user)\n  return \u003Cspan>{user?.name}\u003C\u002Fspan>\n}\n```\n\nAdditional differences:\n\n- **Provider**: Context requires a Provider in the tree; Zustand needs\n  none.\n- **Outside React**: Zustand is accessible without hooks; Context is not.\n- **DevTools**: Zustand supports the Redux DevTools extension; Context\n  has no built-in equivalent.\n- **Bundle overhead**: Context adds zero bytes; Zustand adds ~1 kB.\n\n**Rule of thumb:** Use Context for infrequently-changing cross-cutting\nconcerns (theme, locale, auth token); switch to Zustand when multiple\ncomponents subscribe to frequently-changing shared state.\n",{"id":1476,"difficulty":121,"q":1477,"a":1478},"testing-zustand","How do you test a Zustand store?","Zustand stores are plain JS objects — you can call actions directly and\nassert state without rendering any components.\n\n```js\n\u002F\u002F store.test.js\nimport { describe, it, expect, beforeEach } from 'vitest'\nimport { useCartStore } from '.\u002FcartStore'\n\n\u002F\u002F Reset store state between tests to prevent leakage\nbeforeEach(() => {\n  useCartStore.setState({ items: [] })  \u002F\u002F reset to initial\n})\n\ndescribe('cart store', () => {\n  it('adds an item', () => {\n    useCartStore.getState().addItem({ id: 1, name: 'Book', qty: 1 })\n    expect(useCartStore.getState().items).toHaveLength(1)\n  })\n\n  it('removes an item', () => {\n    useCartStore.setState({ items: [{ id: 1, name: 'Book', qty: 1 }] })\n    useCartStore.getState().removeItem(1)\n    expect(useCartStore.getState().items).toHaveLength(0)\n  })\n})\n```\n\nFor component tests, render normally with `@testing-library\u002Freact` —\nno mocking needed. If you want to test a component in isolation, seed\nthe store with `setState` before rendering and reset with `setState`\nin `beforeEach`.\n\n**Rule of thumb:** Test stores directly via `.getState()` \u002F `.setState()`\nfor unit tests; use the real store (no mocks) for component integration\ntests.\n",{"id":1480,"difficulty":121,"q":1481,"a":1482},"typescript-zustand","How do you add TypeScript types to a Zustand store?","Define an **interface** (or type) for the store shape and pass it as the\ngeneric type argument to `create`. Zustand infers the rest automatically.\n\n```ts\nimport { create } from 'zustand'\n\n\u002F\u002F 1. Define the shape of state + actions\ninterface BearState {\n  bears:     number\n  honey:     number\n  addBear:   () => void\n  eatHoney:  (amount: number) => void\n}\n\n\u002F\u002F 2. Pass the type to create — the callback parameter is typed\nconst useBearStore = create\u003CBearState>()((set, get) => ({\n  bears: 0,\n  honey: 100,\n\n  addBear: () => set((s) => ({ bears: s.bears + 1 })),\n\n  eatHoney: (amount) => {\n    const { honey } = get()\n    if (honey >= amount) set({ honey: honey - amount })\n  },\n}))\n\n\u002F\u002F Selectors are fully typed — TypeScript knows bears is number\nconst bears: number = useBearStore((s) => s.bears)\n```\n\nNote the extra `()` after `create\u003CBearState>()` — this is a TypeScript\ncurrying workaround required to preserve type inference on the callback\nwithout explicitly annotating `set`.\n\n**Rule of thumb:** Always define the store interface first; it acts as\nliving documentation of every field and action the store owns.\n",{"id":1484,"difficulty":96,"q":1485,"a":1486},"when-to-use-zustand","When should you prefer Zustand over component-local state?","**Component-local state** (`useState` \u002F `useReducer`) is the right\ndefault. Lift to Zustand only when one of these conditions holds:\n\n- **Multiple unrelated components** need to read or mutate the same\n  state (avoids prop drilling or a Context that's too coarse).\n- The state **outlives** the component (e.g., persisted cart that should\n  survive route changes).\n- You need to **read or update state outside React** (analytics,\n  WebSocket handlers, service workers).\n- **Performance**: the state changes frequently and many components\n  depend on it — Context would cause a render cascade.\n\n```jsx\n\u002F\u002F ✅ Local state — belongs to one component\nfunction SearchInput() {\n  const [query, setQuery] = useState('')\n  return \u003Cinput value={query} onChange={e => setQuery(e.target.value)} \u002F>\n}\n\n\u002F\u002F ✅ Zustand — notifications consumed by Navbar, Toast, and a WebSocket\nconst useNotifStore = create((set) => ({\n  notifs: [],\n  add:    (n) => set((s) => ({ notifs: [n, ...s.notifs] })),\n  clear:  ()  => set({ notifs: [] }),\n}))\n```\n\n**Rule of thumb:** Default to `useState`; reach for Zustand when state\nneeds to be shared across component boundaries or accessed outside React.\n",{"id":1488,"difficulty":121,"q":1489,"a":1490},"reset-store-state","What is the recommended way to reset a Zustand store to its initial state?","Keep a reference to the **initial state** and call `set` with it in a\n`reset` action. Because `set` does a shallow merge by default, passing\n`true` as the second argument does a full replace, which avoids stale\nkeys if the store shape has changed.\n\n```js\nimport { create } from 'zustand'\n\n\u002F\u002F Capture initial state outside create() so it's always available\nconst initialState = {\n  user:    null,\n  cart:    [],\n  filters: { sort: 'newest', page: 1 },\n}\n\nconst useStore = create((set) => ({\n  ...initialState,\n\n  setUser:  (u) => set({ user: u }),\n  addToCart: (item) => set((s) => ({ cart: [...s.cart, item] })),\n\n  \u002F\u002F Pass true to replace — ensures every key goes back to initial\n  resetAll: () => set(initialState, true),\n\n  \u002F\u002F Or reset only a slice\n  clearCart: () => set({ cart: [] }),\n}))\n\n\u002F\u002F Called at logout\nfunction handleLogout() {\n  useStore.getState().resetAll()\n  router.push('\u002Flogin')\n}\n```\n\nThis pattern is also the standard way to reset stores in tests:\n`useStore.setState(initialState, true)` in a `beforeEach`.\n\n**Rule of thumb:** Always store `initialState` in a const above\n`create()` so resets stay in sync with the store's definition.\n",{"id":1492,"difficulty":121,"q":1493,"a":1494},"derived-computed-values","How do you derive computed values from a Zustand store?","Zustand has no built-in \"computed\" concept. The two idiomatic approaches\nare **inline selector logic** in `useStore()` (for simple derivations)\nand **memoized selectors** via a library like `reselect` or a plain\n`useMemo` (for expensive ones).\n\n```js\nconst useCartStore = create(() => ({\n  items: [\n    { id: 1, name: 'Book',  price: 12, qty: 2 },\n    { id: 2, name: 'Shirt', price: 30, qty: 1 },\n  ],\n}))\n\n\u002F\u002F ─── Option 1: derive inline in the selector (simple, zero extra deps) ───\nfunction CartTotal() {\n  const total = useCartStore((s) =>\n    s.items.reduce((sum, item) => sum + item.price * item.qty, 0)\n  )\n  return \u003Cstrong>Total: ${total}\u003C\u002Fstrong>\n}\n\u002F\u002F Re-renders only when items changes; computation runs on every render\n\n\u002F\u002F ─── Option 2: store a selector factory (avoid recompute if items unchanged)\nimport { useMemo } from 'react'\nfunction CartSummary() {\n  const items = useCartStore((s) => s.items)\n  const total = useMemo(\n    () => items.reduce((sum, i) => sum + i.price * i.qty, 0),\n    [items],  \u002F\u002F only recalculates when items reference changes\n  )\n  return \u003Cp>{items.length} items — ${total}\u003C\u002Fp>\n}\n```\n\nStoring derived values inside the store itself is an anti-pattern —\nthey can get out of sync if the source data is updated directly.\n\n**Rule of thumb:** Keep derived values out of the store; compute them\nin selectors or `useMemo` so they're always consistent with their\nsource.\n",18,{"description":94},"Zustand interview questions — create store, selectors, async actions, devtools, persist middleware, slices pattern, and Zustand vs Redux comparison.","react\u002Fstate-management\u002Fzustand","s8StsaWWxbjyaa7HDzK8qvBUTJnuw_BkENdyoqNrypU",{"id":1501,"title":1502,"body":1503,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1507,"navigation":100,"order":11,"path":1508,"questions":1509,"questionsCount":493,"related":169,"seo":1570,"seoDescription":1571,"stem":1572,"subtopic":1502,"topic":81,"topicSlug":83,"updated":420,"__hash__":1573},"qa\u002Freact\u002Ftesting\u002Fcomponent-interaction-testing.md","Component Interaction Testing",{"type":91,"value":1504,"toc":1505},[],{"title":94,"searchDepth":11,"depth":11,"links":1506},[],{},"\u002Freact\u002Ftesting\u002Fcomponent-interaction-testing",[1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566],{"id":1511,"difficulty":96,"q":1512,"a":1513},"user-click-test","How do you test a button click with React Testing Library?","Use `userEvent.click()` from `@testing-library\u002Fuser-event`. It fires the\nfull sequence of pointer and mouse events a real click produces.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\n\nfunction Counter() {\n  const [count, setCount] = React.useState(0)\n  return (\n    \u003C>\n      \u003Cp>Count: {count}\u003C\u002Fp>\n      \u003Cbutton onClick={() => setCount(c => c + 1)}>Increment\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n\ntest('increments count on click', async () => {\n  const user = userEvent.setup()    \u002F\u002F setup once per test\n  render(\u003CCounter \u002F>)\n\n  expect(screen.getByText('Count: 0')).toBeInTheDocument()\n\n  await user.click(screen.getByRole('button', { name: \u002Fincrement\u002Fi }))\n\n  expect(screen.getByText('Count: 1')).toBeInTheDocument()\n})\n```\n\nKey points:\n- `userEvent.setup()` returns a user instance; create it once per test\n  outside any loops to keep fake timers consistent.\n- `await` every `user.*` call — they're async since user-event v14.\n- Query by role + name so the test doubles as an accessibility check.\n\n**Rule of thumb:** `await user.click()` → assert. One interaction, one\nassertion block. Don't chain multiple un-awaited clicks.\n",{"id":1515,"difficulty":121,"q":1516,"a":1517},"form-submission-test","How do you test form submission?","The most reliable approach is to fill in all fields via `userEvent.type`,\nthen submit via `userEvent.click` on the submit button (or\n`userEvent.keyboard('{Enter}')` if that's how users submit).\n\n```js\nimport { render, screen, waitFor } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\n\ntest('submits registration form', async () => {\n  const handleSubmit = vi.fn()\n  const user = userEvent.setup()\n\n  render(\u003CRegistrationForm onSubmit={handleSubmit} \u002F>)\n\n  await user.type(screen.getByLabelText(\u002Femail\u002Fi), 'user@example.com')\n  await user.type(screen.getByLabelText(\u002Fpassword\u002Fi), 'secret123')\n  await user.click(screen.getByRole('button', { name: \u002Fregister\u002Fi }))\n\n  await waitFor(() => {\n    expect(handleSubmit).toHaveBeenCalledWith({\n      email: 'user@example.com',\n      password: 'secret123',\n    })\n  })\n})\n```\n\nFor native form submission (no JS handler), you can also listen for the\n`submit` event on the form element:\n```js\nconst handleSubmit = vi.fn(e => e.preventDefault())\nrender(\u003Cform onSubmit={handleSubmit}>\u003Cbutton type=\"submit\">Go\u003C\u002Fbutton>\u003C\u002Fform>)\nawait user.click(screen.getByRole('button', { name: \u002Fgo\u002Fi }))\nexpect(handleSubmit).toHaveBeenCalled()\n```\n\n**Rule of thumb:** Test the *outcome* of submission (callback called,\nsuccess message shown, navigation triggered) rather than testing the\nform's internal state.\n",{"id":1519,"difficulty":96,"q":1520,"a":1521},"controlled-input-test","How do you test a controlled input that updates as the user types?","`userEvent.type()` fires a keydown\u002Fkeypress\u002Finput\u002Fkeyup sequence for\neach character, causing controlled inputs to update through the standard\nReact synthetic event flow.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\n\nfunction SearchBox() {\n  const [query, setQuery] = React.useState('')\n  return (\n    \u003Cinput\n      aria-label=\"Search\"\n      value={query}\n      onChange={e => setQuery(e.target.value)}\n    \u002F>\n  )\n}\n\ntest('updates value as user types', async () => {\n  const user = userEvent.setup()\n  render(\u003CSearchBox \u002F>)\n\n  const input = screen.getByRole('textbox', { name: \u002Fsearch\u002Fi })\n  await user.type(input, 'react hooks')\n\n  expect(input).toHaveValue('react hooks')\n})\n```\n\nTo clear an existing value before typing, use `user.clear()`:\n```js\nawait user.clear(input)\nawait user.type(input, 'new value')\n```\n\nTo type into a `\u003Cselect>`:\n```js\nawait user.selectOptions(screen.getByRole('combobox'), 'Option B')\nexpect(screen.getByRole('combobox')).toHaveDisplayValue('Option B')\n```\n\n**Rule of thumb:** Prefer `user.type()` over manually setting\n`.value` + dispatching a change event — it tests the full keyboard\ninteraction path.\n",{"id":1523,"difficulty":96,"q":1524,"a":1525},"conditional-rendering-test","How do you test conditional rendering?","Assert presence with `getByRole`\u002F`getByText` and absence with\n`queryBy*` returning `null` or `not.toBeInTheDocument()`.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\n\nfunction Alert({ type, message }) {\n  if (!message) return null\n  return \u003Cdiv role=\"alert\" className={`alert-${type}`}>{message}\u003C\u002Fdiv>\n}\n\ntest('renders nothing when message is empty', () => {\n  render(\u003CAlert type=\"error\" message=\"\" \u002F>)\n  expect(screen.queryByRole('alert')).not.toBeInTheDocument()\n})\n\ntest('renders error message', () => {\n  render(\u003CAlert type=\"error\" message=\"Something failed\" \u002F>)\n  expect(screen.getByRole('alert')).toHaveTextContent('Something failed')\n})\n```\n\nTesting a toggle:\n```js\ntest('shows\u002Fhides details on button click', async () => {\n  const user = userEvent.setup()\n  render(\u003CExpandableSection title=\"Details\" content=\"Hidden text\" \u002F>)\n\n  expect(screen.queryByText('Hidden text')).not.toBeInTheDocument()\n\n  await user.click(screen.getByRole('button', { name: \u002Fdetails\u002Fi }))\n  expect(screen.getByText('Hidden text')).toBeInTheDocument()\n\n  await user.click(screen.getByRole('button', { name: \u002Fdetails\u002Fi }))\n  expect(screen.queryByText('Hidden text')).not.toBeInTheDocument()\n})\n```\n\n**Rule of thumb:** Always use `queryBy*` (not `getBy*`) when asserting\nan element is absent — `getBy*` throws before your assertion runs.\n",{"id":1527,"difficulty":121,"q":1528,"a":1529},"context-provider-test","How do you test a component that consumes React context?","Wrap the component with the real provider (not a mock) in the `wrapper`\noption or a custom render helper.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { ThemeProvider } from '.\u002FThemeContext'\nimport ThemeToggle from '.\u002FThemeToggle'\n\nfunction renderWithTheme(ui, { theme = 'light' } = {}) {\n  return render(ui, {\n    wrapper: ({ children }) => (\n      \u003CThemeProvider initialTheme={theme}>{children}\u003C\u002FThemeProvider>\n    ),\n  })\n}\n\ntest('switches to dark mode', async () => {\n  const user = userEvent.setup()\n  renderWithTheme(\u003CThemeToggle \u002F>)\n\n  expect(document.body).toHaveClass('light')\n  await user.click(screen.getByRole('button', { name: \u002Fdark mode\u002Fi }))\n  expect(document.body).toHaveClass('dark')\n})\n\ntest('reads initial theme from context', () => {\n  renderWithTheme(\u003CThemeToggle \u002F>, { theme: 'dark' })\n  expect(screen.getByRole('button', { name: \u002Flight mode\u002Fi })).toBeInTheDocument()\n})\n```\n\nAvoid mocking the context value directly (`vi.mock`) unless the context\ncomes from a third-party library you can't control. Testing through the\nreal provider catches integration bugs between the provider and consumer.\n\n**Rule of thumb:** Use the real provider with controlled initial values;\nmock only external context sources (auth libraries, feature-flag SDKs).\n",{"id":1531,"difficulty":121,"q":1532,"a":1533},"router-testing","How do you test components that use React Router hooks like `useNavigate` or `useParams`?","Wrap with `MemoryRouter` (or `createMemoryRouter` + `RouterProvider` for\nv6 data routers) so the component has a router context without a real\nbrowser history.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { MemoryRouter, Route, Routes } from 'react-router-dom'\n\n\u002F\u002F Testing useParams\ntest('renders product with correct id', () => {\n  render(\n    \u003CMemoryRouter initialEntries={['\u002Fproducts\u002F42']}>\n      \u003CRoutes>\n        \u003CRoute path=\"\u002Fproducts\u002F:id\" element={\u003CProductPage \u002F>} \u002F>\n      \u003C\u002FRoutes>\n    \u003C\u002FMemoryRouter>\n  )\n  expect(screen.getByText('Product 42')).toBeInTheDocument()\n})\n\n\u002F\u002F Testing useNavigate\ntest('navigates to home after logout', async () => {\n  const user = userEvent.setup()\n  render(\n    \u003CMemoryRouter initialEntries={['\u002Fdashboard']}>\n      \u003CRoutes>\n        \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n        \u003CRoute path=\"\u002F\" element={\u003Cp>Home\u003C\u002Fp>} \u002F>\n      \u003C\u002FRoutes>\n    \u003C\u002FMemoryRouter>\n  )\n\n  await user.click(screen.getByRole('button', { name: \u002Flogout\u002Fi }))\n  expect(screen.getByText('Home')).toBeInTheDocument()\n})\n```\n\nFor components using the newer data router APIs, use\n`createMemoryRouter` + `RouterProvider`:\n```js\nimport { createMemoryRouter, RouterProvider } from 'react-router-dom'\n\nconst router = createMemoryRouter(routes, { initialEntries: ['\u002Fdashboard'] })\nrender(\u003CRouterProvider router={router} \u002F>)\n```\n\n**Rule of thumb:** `MemoryRouter` for simple path\u002Fparam tests;\n`createMemoryRouter` when you need loaders, actions, or data-router\nfeatures.\n",{"id":1535,"difficulty":96,"q":1536,"a":1537},"list-rendering-test","How do you test that a list renders the correct number of items?","Query all list items with `getAllBy*` or `queryAllBy*` and check the\narray length, then assert individual items by their content.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\n\nconst todos = [\n  { id: 1, text: 'Buy milk' },\n  { id: 2, text: 'Walk dog' },\n  { id: 3, text: 'Read book' },\n]\n\ntest('renders correct number of todo items', () => {\n  render(\u003CTodoList todos={todos} \u002F>)\n\n  const items = screen.getAllByRole('listitem')\n  expect(items).toHaveLength(3)\n})\n\ntest('renders each todo text', () => {\n  render(\u003CTodoList todos={todos} \u002F>)\n\n  expect(screen.getByText('Buy milk')).toBeInTheDocument()\n  expect(screen.getByText('Walk dog')).toBeInTheDocument()\n  expect(screen.getByText('Read book')).toBeInTheDocument()\n})\n\ntest('shows empty state when no todos', () => {\n  render(\u003CTodoList todos={[]} \u002F>)\n  expect(screen.getByText(\u002Fno todos yet\u002Fi)).toBeInTheDocument()\n  expect(screen.queryAllByRole('listitem')).toHaveLength(0)\n})\n```\n\n**Rule of thumb:** Test the count and a sample of items; don't assert\nevery item in a large list — that couples tests to data and makes them\nbrittle.\n",{"id":1539,"difficulty":121,"q":1540,"a":1541},"modal-portal-test","How do you test a component that renders into a portal (`ReactDOM.createPortal`)?","RTL's `screen` queries target `document.body`, which includes portal\ncontent — no special setup needed.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport Modal from '.\u002FModal'\n\n\u002F\u002F Modal renders into document.body via createPortal\ntest('opens and closes modal', async () => {\n  const user = userEvent.setup()\n  render(\u003CModal trigger={\u003Cbutton>Open\u003C\u002Fbutton>}>Dialog content\u003C\u002FModal>)\n\n  \u002F\u002F Before open — modal not in DOM\n  expect(screen.queryByRole('dialog')).not.toBeInTheDocument()\n\n  \u002F\u002F Open the modal\n  await user.click(screen.getByRole('button', { name: \u002Fopen\u002Fi }))\n  expect(screen.getByRole('dialog')).toBeInTheDocument()\n  expect(screen.getByText('Dialog content')).toBeInTheDocument()\n\n  \u002F\u002F Close with Escape\n  await user.keyboard('{Escape}')\n  expect(screen.queryByRole('dialog')).not.toBeInTheDocument()\n})\n```\n\nIf the portal target is a custom element (not `document.body`), you may\nneed to create it in setup:\n```js\nbeforeEach(() => {\n  const el = document.createElement('div')\n  el.setAttribute('id', 'modal-root')\n  document.body.appendChild(el)\n})\nafterEach(() => {\n  document.getElementById('modal-root')?.remove()\n})\n```\n\n**Rule of thumb:** Test portals through `screen` like any other\nelement — their implementation detail (portal vs inline) is irrelevant\nto user-facing behavior.\n",{"id":1543,"difficulty":121,"q":1544,"a":1545},"keyboard-navigation-test","How do you test keyboard navigation and focus management?","Use `userEvent.keyboard()` or `userEvent.tab()` to simulate keyboard\ninput and check focus with `toHaveFocus()`.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\n\ntest('traps focus inside modal', async () => {\n  const user = userEvent.setup()\n  render(\u003CModal open>\u003Cinput aria-label=\"Name\" \u002F>\u003Cbutton>Close\u003C\u002Fbutton>\u003C\u002FModal>)\n\n  const nameInput = screen.getByRole('textbox', { name: \u002Fname\u002Fi })\n  const closeButton = screen.getByRole('button', { name: \u002Fclose\u002Fi })\n\n  \u002F\u002F Focus starts on first focusable element\n  expect(nameInput).toHaveFocus()\n\n  \u002F\u002F Tab moves to next element\n  await user.tab()\n  expect(closeButton).toHaveFocus()\n\n  \u002F\u002F Tab wraps back to first element (focus trap)\n  await user.tab()\n  expect(nameInput).toHaveFocus()\n})\n\ntest('closes dropdown on Escape', async () => {\n  const user = userEvent.setup()\n  render(\u003CDropdown label=\"Options\">\u003Cli>Item 1\u003C\u002Fli>\u003C\u002FDropdown>)\n\n  await user.click(screen.getByRole('button', { name: \u002Foptions\u002Fi }))\n  expect(screen.getByRole('listbox')).toBeInTheDocument()\n\n  await user.keyboard('{Escape}')\n  expect(screen.queryByRole('listbox')).not.toBeInTheDocument()\n})\n```\n\nUseful keyboard shortcuts for `user.keyboard()`:\n```\n'{Tab}'    '{Shift>}{Tab}{\u002FShift}'    '{Enter}'\n'{Space}'  '{Escape}'                  '{ArrowDown}'\n```\n\n**Rule of thumb:** Always test keyboard navigation for interactive\ncomponents (modals, dropdowns, menus) — it's both a UX and accessibility\nrequirement.\n",{"id":1547,"difficulty":208,"q":1548,"a":1549},"error-boundary-test","How do you test error boundaries?","Render a child component that throws inside an error boundary, and assert\nthe fallback UI. Since React logs errors to `console.error`, suppress\nthat to keep test output clean.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport ErrorBoundary from '.\u002FErrorBoundary'\n\nfunction ThrowOnMount() {\n  throw new Error('Test error')\n}\n\ntest('renders fallback when child throws', () => {\n  \u002F\u002F Suppress React's error output in test logs\n  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\n\n  render(\n    \u003CErrorBoundary fallback={\u003Cp>Something went wrong\u003C\u002Fp>}>\n      \u003CThrowOnMount \u002F>\n    \u003C\u002FErrorBoundary>\n  )\n\n  expect(screen.getByText('Something went wrong')).toBeInTheDocument()\n  consoleSpy.mockRestore()\n})\n\ntest('renders children when no error', () => {\n  render(\n    \u003CErrorBoundary fallback={\u003Cp>Something went wrong\u003C\u002Fp>}>\n      \u003Cp>All good\u003C\u002Fp>\n    \u003C\u002FErrorBoundary>\n  )\n  expect(screen.getByText('All good')).toBeInTheDocument()\n  expect(screen.queryByText('Something went wrong')).not.toBeInTheDocument()\n})\n```\n\nFor error boundaries with a \"retry\" button, update the child after\nerror to test recovery:\n```js\ntest('recovers after reset', async () => {\n  const user = userEvent.setup()\n  let shouldThrow = true\n  function MaybeThrow() {\n    if (shouldThrow) throw new Error('oops')\n    return \u003Cp>Recovered\u003C\u002Fp>\n  }\n  \u002F\u002F ... render, suppress, click retry, update shouldThrow, assert\n})\n```\n\n**Rule of thumb:** Always mock `console.error` in error boundary tests —\nRTL and React both log thrown errors, cluttering test output without it.\n",{"id":1551,"difficulty":208,"q":1552,"a":1553},"integration-test-pattern","How do you write an integration test covering multiple components together?","Render the top-level component that composes the components you want to\ntest together, provide any required context or mocks at the boundary,\nand simulate the full user flow from start to finish.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { MemoryRouter } from 'react-router-dom'\nimport { rest } from 'msw'\nimport { setupServer } from 'msw\u002Fnode'\nimport App from '.\u002FApp'\n\nconst server = setupServer(\n  rest.post('\u002Fapi\u002Flogin', (req, res, ctx) =>\n    res(ctx.json({ token: 'abc123', user: { name: 'Alice' } }))\n  )\n)\n\nbeforeAll(() => server.listen())\nafterEach(() => server.resetHandlers())\nafterAll(() => server.close())\n\ntest('full login flow: form → API → dashboard', async () => {\n  const user = userEvent.setup()\n\n  render(\n    \u003CMemoryRouter initialEntries={['\u002Flogin']}>\n      \u003CApp \u002F>\n    \u003C\u002FMemoryRouter>\n  )\n\n  \u002F\u002F Fill login form\n  await user.type(screen.getByLabelText(\u002Femail\u002Fi), 'alice@example.com')\n  await user.type(screen.getByLabelText(\u002Fpassword\u002Fi), 'secret')\n  await user.click(screen.getByRole('button', { name: \u002Fsign in\u002Fi }))\n\n  \u002F\u002F After successful login, should show dashboard\n  expect(await screen.findByText('Welcome, Alice')).toBeInTheDocument()\n  expect(screen.queryByRole('form')).not.toBeInTheDocument()\n})\n```\n\nIntegration tests give the highest confidence but are slower and harder\nto debug. Strategy:\n- Unit tests for complex logic (reducers, utilities).\n- Component tests for individual component behavior.\n- Integration tests for critical user flows (login, checkout, onboarding).\n\n**Rule of thumb:** Write integration tests for flows that cross component\nboundaries and involve real user journeys, not for individual component\nrendering.\n",{"id":1555,"difficulty":96,"q":1556,"a":1557},"spy-prop-function","How do you test that a callback prop is called with the right arguments?","Pass a `vi.fn()` (or `jest.fn()`) as the prop and assert on it after\nthe interaction.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { vi, expect } from 'vitest'\nimport ProductCard from '.\u002FProductCard'\n\ntest('calls onAddToCart with product id when button clicked', async () => {\n  const onAddToCart = vi.fn()\n  const user = userEvent.setup()\n\n  render(\u003CProductCard id={42} name=\"Widget\" onAddToCart={onAddToCart} \u002F>)\n\n  await user.click(screen.getByRole('button', { name: \u002Fadd to cart\u002Fi }))\n\n  expect(onAddToCart).toHaveBeenCalledTimes(1)\n  expect(onAddToCart).toHaveBeenCalledWith(42)\n})\n```\n\nFor multiple interactions:\n```js\nawait user.click(incrementBtn)\nawait user.click(incrementBtn)\nexpect(onChange).toHaveBeenCalledTimes(2)\nexpect(onChange).toHaveBeenNthCalledWith(1, 1)\nexpect(onChange).toHaveBeenNthCalledWith(2, 2)\n```\n\n**Rule of thumb:** Use `vi.fn()` for callback props; use `toHaveBeenCalledWith`\nrather than checking internal state — you're testing the contract between\nparent and child.\n",{"id":1559,"difficulty":96,"q":1560,"a":1561},"select-checkbox-test","How do you test checkboxes, radio buttons, and select elements?","RTL has dedicated `userEvent` methods for each form element type.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\n\nconst user = userEvent.setup()\n\n\u002F\u002F Checkbox\ntest('toggles checkbox', async () => {\n  render(\u003Clabel>\u003Cinput type=\"checkbox\" \u002F>Accept terms\u003C\u002Flabel>)\n  const checkbox = screen.getByRole('checkbox', { name: \u002Faccept terms\u002Fi })\n\n  expect(checkbox).not.toBeChecked()\n  await user.click(checkbox)\n  expect(checkbox).toBeChecked()\n  await user.click(checkbox)\n  expect(checkbox).not.toBeChecked()\n})\n\n\u002F\u002F Radio buttons\ntest('selects radio option', async () => {\n  render(\n    \u003Cfieldset>\n      \u003Clegend>Size\u003C\u002Flegend>\n      \u003Clabel>\u003Cinput type=\"radio\" name=\"size\" value=\"S\" \u002F>Small\u003C\u002Flabel>\n      \u003Clabel>\u003Cinput type=\"radio\" name=\"size\" value=\"L\" \u002F>Large\u003C\u002Flabel>\n    \u003C\u002Ffieldset>\n  )\n  await user.click(screen.getByRole('radio', { name: \u002Fsmall\u002Fi }))\n  expect(screen.getByRole('radio', { name: \u002Fsmall\u002Fi })).toBeChecked()\n  expect(screen.getByRole('radio', { name: \u002Flarge\u002Fi })).not.toBeChecked()\n})\n\n\u002F\u002F Select\ntest('selects option from dropdown', async () => {\n  render(\n    \u003Cselect aria-label=\"Country\">\n      \u003Coption value=\"\">--\u003C\u002Foption>\n      \u003Coption value=\"us\">US\u003C\u002Foption>\n      \u003Coption value=\"ca\">Canada\u003C\u002Foption>\n    \u003C\u002Fselect>\n  )\n  await user.selectOptions(screen.getByRole('combobox', { name: \u002Fcountry\u002Fi }), 'us')\n  expect(screen.getByRole('combobox', { name: \u002Fcountry\u002Fi })).toHaveDisplayValue('US')\n})\n```\n\n**Rule of thumb:** Always query form elements by their accessible role +\nname (derived from `\u003Clabel>`) — this forces you to write accessible markup.\n",{"id":1563,"difficulty":96,"q":1564,"a":1565},"disabled-element-test","How do you test that a button or input is disabled, and that it can't be interacted with?","Assert the `disabled` state with `toBeDisabled()` and verify that\nclicking a disabled button does not invoke callbacks.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\n\ntest('submit button is disabled while loading', () => {\n  render(\u003CSubmitButton loading={true} \u002F>)\n  expect(screen.getByRole('button', { name: \u002Fsubmit\u002Fi })).toBeDisabled()\n})\n\ntest('disabled button does not call onClick', async () => {\n  const onClick = vi.fn()\n  const user = userEvent.setup()\n\n  render(\u003Cbutton disabled onClick={onClick}>Click me\u003C\u002Fbutton>)\n\n  await user.click(screen.getByRole('button'))\n  \u002F\u002F userEvent respects the disabled attribute and does not fire the click\n  expect(onClick).not.toHaveBeenCalled()\n})\n\ntest('submit button becomes enabled after required fields filled', async () => {\n  const user = userEvent.setup()\n  render(\u003CForm \u002F>)\n\n  const button = screen.getByRole('button', { name: \u002Fsubmit\u002Fi })\n  expect(button).toBeDisabled()\n\n  await user.type(screen.getByLabelText(\u002Femail\u002Fi), 'user@test.com')\n  expect(button).toBeEnabled()\n})\n```\n\n**Rule of thumb:** `toBeDisabled()` and `toBeEnabled()` from\n`@testing-library\u002Fjest-dom` are clearer than `toHaveAttribute('disabled')`.\n",{"id":1567,"difficulty":96,"q":1568,"a":1569},"snapshot-testing","What is snapshot testing and when should (and shouldn't) you use it with RTL?","Snapshot testing captures the rendered output as a string on first run and\ncompares future runs against that snapshot. Vitest\u002FJest provide this via\n`toMatchSnapshot()` or `toMatchInlineSnapshot()`.\n\n```js\nimport { render } from '@testing-library\u002Freact'\n\ntest('renders badge correctly', () => {\n  const { container } = render(\u003CBadge count={5} \u002F>)\n  expect(container.firstChild).toMatchSnapshot()\n})\n```\n\nRTL's stance: **use sparingly**. Snapshots are good for:\n- Preventing unintentional markup changes in leaf UI components.\n- Locking down third-party rendered output you don't control.\n\nSnapshots are bad for:\n- Testing behavior — they catch \"it changed\", not \"it's wrong\".\n- Large components — snapshots become huge and are mindlessly updated.\n- Fast-moving codebases — you spend more time updating snapshots than\n  fixing real bugs.\n\nPrefer inline snapshots when you do use them:\n```js\nexpect(container.firstChild).toMatchInlineSnapshot(`\n  \u003Cspan class=\"badge badge--blue\">5\u003C\u002Fspan>\n`)\n```\n\n**Rule of thumb:** Write behavioral assertions (text content, role,\nvisibility) instead of snapshots. Use snapshots only for small, stable,\npurely presentational components.\n",{"description":94},"React Testing Library interview questions on component interaction — user events, form testing, conditional rendering, context, portals, keyboard navigation, and integration testing patterns.","react\u002Ftesting\u002Fcomponent-interaction-testing","-AUbvLNtLgCYWx1KFc_HmPgK-UBn7Rgfrv2MfaNc8PY",{"id":1575,"title":1576,"body":1577,"description":94,"difficulty":96,"extension":97,"framework":10,"frameworkSlug":8,"meta":1581,"navigation":100,"order":37,"path":1583,"questions":1584,"questionsCount":1652,"related":169,"seo":1653,"seoDescription":1654,"stem":1655,"subtopic":1576,"topic":28,"topicSlug":29,"updated":174,"__hash__":1656},"qa\u002Freact\u002Fcomponents\u002Fevent-handling.md","Event Handling",{"type":91,"value":1578,"toc":1579},[],{"title":94,"searchDepth":11,"depth":11,"links":1580},[],{"subtopicSlug":1582},"event-handling","\u002Freact\u002Fcomponents\u002Fevent-handling",[1585,1589,1593,1597,1601,1605,1609,1613,1617,1621,1624,1628,1632,1636,1640,1644,1648],{"id":1586,"difficulty":96,"q":1587,"a":1588},"attach-event-handler","How do you attach an event handler to an element in React?","Pass a **function reference** as a camelCase prop on the JSX element.\nReact registers the handler for you via its internal event system.\n\n```jsx\nfunction ClickCounter() {\n  const [count, setCount] = useState(0)\n\n  function handleClick() {\n    setCount(c => c + 1)\n  }\n\n  return \u003Cbutton onClick={handleClick}>Clicked {count} times\u003C\u002Fbutton>\n}\n```\n\nCommon pitfalls:\n- `onClick={handleClick()}` — calls the function immediately during render\n  and passes its return value. Correct form omits the `()`.\n- Arrow functions inline are fine but create a new function each render,\n  which can matter for memoized children.\n\n**Rule of thumb:** name handlers `handleEventName` by convention; pass\nthem as `onEventName={handler}` — no parentheses.\n",{"id":1590,"difficulty":121,"q":1591,"a":1592},"synthetic-event","What is a SyntheticEvent in React?","React wraps native browser events in a **SyntheticEvent** — a cross-browser\nwrapper that normalizes differences in the native event API (e.g. IE vs\nChrome). It exposes the same interface as the native event\n(`target`, `preventDefault()`, `stopPropagation()`, etc.) but works\nconsistently everywhere.\n\n```jsx\nfunction Input() {\n  function handleChange(e) {\n    \u002F\u002F e is a SyntheticEvent\n    console.log(e.target.value)   \u002F\u002F same API as native InputEvent\n    console.log(e.nativeEvent)    \u002F\u002F access the raw browser event\n  }\n  return \u003Cinput onChange={handleChange} \u002F>\n}\n```\n\n**React 17 change:** prior to React 17, synthetic events were **pooled**\n(reused after the handler returned, with all properties set to `null`). In\nReact 17+, pooling was removed — you can safely access the event\nasynchronously.\n\n**Rule of thumb:** treat a SyntheticEvent exactly like a native DOM event.\nIf you need the raw event, it's on `e.nativeEvent`.\n",{"id":1594,"difficulty":96,"q":1595,"a":1596},"camelcase-events","Why do React event names use camelCase?","Because JSX is JavaScript, not HTML. In HTML, event attributes are lowercase\nstrings (`onclick`, `onmouseover`). In JSX, they are **JavaScript property\nnames** on the props object, and the React team chose camelCase as the\nconvention to match JavaScript idioms.\n\n```jsx\n\u002F\u002F HTML\n\u002F\u002F \u003Cbutton onclick=\"handler()\">\n\n\u002F\u002F JSX\n\u003Cbutton onClick={handler}>\n\n\u002F\u002F More examples\n\u003Cinput onChange={handle} onFocus={handle} onBlur={handle} \u002F>\n\u003Cform onSubmit={handle} \u002F>\n\u003Cdiv onMouseEnter={handle} onMouseLeave={handle} \u002F>\n```\n\n**Rule of thumb:** every DOM event is available in React as `on` +\nPascalCase (e.g. `click` → `onClick`, `keydown` → `onKeyDown`).\n",{"id":1598,"difficulty":96,"q":1599,"a":1600},"prevent-default","How do you prevent default browser behavior in React?","Call `e.preventDefault()` on the SyntheticEvent inside your handler.\n\n```jsx\nfunction LoginForm() {\n  function handleSubmit(e) {\n    e.preventDefault()          \u002F\u002F stops the full-page form POST\n    \u002F\u002F ... handle submission with fetch\n  }\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput name=\"email\" type=\"email\" \u002F>\n      \u003Cbutton type=\"submit\">Log in\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\nCommon use cases:\n- `\u003Cform>` `onSubmit` — prevent page reload.\n- `\u003Ca>` `onClick` — prevent navigation to `href`.\n- Drag events — prevent the default drop behavior.\n\n**Rule of thumb:** call `preventDefault()` at the top of your handler\nbefore any async work — after an `await`, the event may have already\nbeen handled by the browser.\n",{"id":1602,"difficulty":121,"q":1603,"a":1604},"return-false","Why can't you return false to prevent default behavior in React?","In plain HTML event attributes (`onclick=\"return false\"`), returning `false`\nwas a shortcut that both prevented the default and stopped propagation.\nReact **does not** support this shortcut.\n\nReact event handlers are regular JavaScript functions — returning `false`\nfrom them has no special meaning to React's event system.\n\n```jsx\n\u002F\u002F ❌ Doesn't work in React — return value is ignored\n\u003Ca href=\"\u002Fhome\" onClick={() => false}>Go\u003C\u002Fa>\n\n\u002F\u002F ✅ Explicit calls are required\n\u003Ca\n  href=\"\u002Fhome\"\n  onClick={e => {\n    e.preventDefault()\n    e.stopPropagation()\n  }}\n>\n  Go\n\u003C\u002Fa>\n```\n\n**Rule of thumb:** always call `e.preventDefault()` and\u002For\n`e.stopPropagation()` explicitly; never rely on return values.\n",{"id":1606,"difficulty":121,"q":1607,"a":1608},"stop-propagation","What is the difference between stopPropagation and preventDefault?","They control two completely different things:\n\n- **`e.preventDefault()`** — stops the browser from running its built-in\n  action for the event (e.g. form submission, link navigation, checkbox\n  toggle). The event still bubbles up through the DOM.\n\n- **`e.stopPropagation()`** — stops the event from bubbling further up the\n  DOM tree. Parent handlers won't fire. Does not affect the browser action.\n\n```jsx\nfunction List() {\n  return (\n    \u003Cul onClick={() => console.log('list clicked')}>\n      \u003Cli>\n        \u003Ca\n          href=\"\u002Fdetail\"\n          onClick={e => {\n            e.preventDefault()    \u002F\u002F don't navigate to \u002Fdetail\n            e.stopPropagation()   \u002F\u002F don't trigger the ul's onClick\n            navigate('\u002Fmodal')\n          }}\n        >\n          View\n        \u003C\u002Fa>\n      \u003C\u002Fli>\n    \u003C\u002Ful>\n  )\n}\n```\n\n**Rule of thumb:** call `preventDefault` to suppress browser behavior;\ncall `stopPropagation` to prevent parent handlers from also running.\nYou often need both for nested interactive elements.\n",{"id":1610,"difficulty":121,"q":1611,"a":1612},"passing-arguments","How do you pass arguments to an event handler?","Wrap the handler in an arrow function or use `.bind`. The arrow-function\napproach is more common in JSX.\n\n```jsx\nfunction ItemList({ items }) {\n  function handleDelete(id, e) {\n    e.stopPropagation()\n    deleteItem(id)\n  }\n\n  return (\n    \u003Cul>\n      {items.map(item => (\n        \u003Cli key={item.id}>\n          {item.name}\n          {\u002F* Arrow wrapper — creates a new fn per render *\u002F}\n          \u003Cbutton onClick={e => handleDelete(item.id, e)}>Delete\u003C\u002Fbutton>\n        \u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n```\n\nFor performance-sensitive lists, you can use data attributes instead:\n\n```jsx\nfunction handleDelete(e) {\n  const id = e.currentTarget.dataset.id\n  deleteItem(id)\n}\n\n\u003Cbutton data-id={item.id} onClick={handleDelete}>Delete\u003C\u002Fbutton>\n```\n\n**Rule of thumb:** arrow wrappers are fine for most use cases. Use\n`data-*` attributes on large lists where creating many closures is\nmeasurably expensive.\n",{"id":1614,"difficulty":121,"q":1615,"a":1616},"event-delegation","How does event delegation work in React?","React does not attach individual event listeners directly to each DOM node.\nInstead, it attaches **a single listener** at the root and uses event\nbubbling to catch all events — this is called event delegation.\n\nPrior to React 17, all events were delegated to `document`. In React 17+,\nthey are delegated to the **root DOM container** (`document.getElementById('root')`).\nThis change makes it safer to embed multiple React apps on one page or mix\nReact with non-React code.\n\n```jsx\n\u002F\u002F Conceptually what React does internally (simplified)\nrootElement.addEventListener('click', react_internal_handler)\n\u002F\u002F When a \u003Cbutton> inside is clicked, the event bubbles up to root,\n\u002F\u002F React checks which component was the target, and calls your onClick.\n```\n\nThis is largely transparent to application code, but matters when:\n- Calling `e.stopPropagation()` — stops bubbling before it reaches the\n  React root, so React never sees the event.\n- Using `addEventListener` directly on `document` — fires after React's\n  handler in React 17+.\n\n**Rule of thumb:** rely on React's event system rather than attaching your\nown listeners to `document`; it handles delegation and cleanup for you.\n",{"id":1618,"difficulty":96,"q":1619,"a":1620},"form-handling","How do you handle a form submission in React?","Attach `onSubmit` to the `\u003Cform>` element, call `e.preventDefault()` to\nstop the page reload, then read values from controlled state or uncontrolled\nrefs.\n\n```jsx\nfunction SignupForm() {\n  const [email, setEmail] = useState('')\n  const [password, setPassword] = useState('')\n\n  async function handleSubmit(e) {\n    e.preventDefault()\n    await signUp({ email, password })\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput\n        type=\"email\"\n        value={email}\n        onChange={e => setEmail(e.target.value)}\n      \u002F>\n      \u003Cinput\n        type=\"password\"\n        value={password}\n        onChange={e => setPassword(e.target.value)}\n      \u002F>\n      \u003Cbutton type=\"submit\">Sign up\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n**Rule of thumb:** always put `onSubmit` on `\u003Cform>`, not `onClick` on\nthe submit button — the form's handler fires for Enter key presses as well\nas button clicks.\n",{"id":244,"difficulty":121,"q":1622,"a":1623},"What is a controlled input and why does React prefer it?","A **controlled input** has its `value` (or `checked`) driven by React\nstate. React becomes the single source of truth for the input's content.\n\n```jsx\n\u002F\u002F Controlled — React owns the value\nconst [text, setText] = useState('')\n\u003Cinput value={text} onChange={e => setText(e.target.value)} \u002F>\n\n\u002F\u002F Uncontrolled — DOM owns the value, React reads it on demand\nconst ref = useRef()\n\u003Cinput ref={ref} \u002F>\n\u002F\u002F later: ref.current.value\n```\n\nWhy React prefers controlled:\n- You can validate or transform input on every keystroke.\n- Input state is immediately available in component scope — no need to\n  query the DOM.\n- The input reflects state exactly, so programmatic resets work:\n  `setText('')` clears the field.\n\n**Rule of thumb:** use controlled inputs by default; use uncontrolled when\nintegrating with non-React DOM libraries or when you only need the value\nat submission.\n",{"id":1625,"difficulty":121,"q":1626,"a":1627},"onchange-vs-oninput","What is the difference between onChange and onInput in React?","In the browser, `input` fires on every value change (typing, pasting,\ncutting), while `change` fires only when the element loses focus after a\nvalue change.\n\nReact **normalizes** this: React's `onChange` fires on every value change\n(like the native `input` event), making it consistent regardless of element\ntype. React's `onChange` and native `oninput` are effectively equivalent for\ntext inputs.\n\n```jsx\n\u002F\u002F Fires on every keystroke in React (not just on blur like native 'change')\n\u003Cinput onChange={e => console.log(e.target.value)} \u002F>\n```\n\nReact does expose `onInput` as well, but for controlled inputs `onChange`\nis the standard and recommended handler.\n\n**Rule of thumb:** use `onChange` in React for real-time input tracking.\nKnowing that it behaves like native `oninput` matters when comparing React\nbehavior with vanilla JS.\n",{"id":1629,"difficulty":121,"q":1630,"a":1631},"keyboard-events","How do you handle keyboard events in React?","Use `onKeyDown`, `onKeyUp`, or `onKeyPress` (deprecated — avoid). Read\n`e.key` for the logical key name or `e.code` for the physical key.\n\n```jsx\nfunction SearchBox() {\n  const [query, setQuery] = useState('')\n\n  function handleKeyDown(e) {\n    if (e.key === 'Enter') {\n      search(query)\n    }\n    if (e.key === 'Escape') {\n      setQuery('')\n    }\n  }\n\n  return (\n    \u003Cinput\n      value={query}\n      onChange={e => setQuery(e.target.value)}\n      onKeyDown={handleKeyDown}\n    \u002F>\n  )\n}\n```\n\nFor accessibility, elements that handle keyboard events should also be\nfocusable (native interactive elements are by default; custom elements need\n`tabIndex={0}` and appropriate ARIA roles).\n\n**Rule of thumb:** check `e.key` (logical, locale-aware) rather than\n`e.keyCode` (deprecated numeric code). For modifier keys, check\n`e.ctrlKey`, `e.shiftKey`, `e.altKey`, `e.metaKey`.\n",{"id":1633,"difficulty":121,"q":1634,"a":1635},"class-this-binding","How does 'this' binding work in class component event handlers?","In a class component, `this` inside a regular method is `undefined` when\ncalled as an event handler (because it's passed as a callback — detached\nfrom the instance). There are three common fixes:\n\n```jsx\nclass Counter extends React.Component {\n  constructor(props) {\n    super(props)\n    this.state = { n: 0 }\n    \u002F\u002F Option 1: bind in constructor\n    this.handleClick = this.handleClick.bind(this)\n  }\n\n  handleClick() {\n    this.setState({ n: this.state.n + 1 })\n  }\n\n  render() {\n    return \u003Cbutton onClick={this.handleClick}>{this.state.n}\u003C\u002Fbutton>\n  }\n}\n\n\u002F\u002F Option 2: class field arrow function (auto-binds)\nclass Counter extends React.Component {\n  state = { n: 0 }\n  handleClick = () => {\n    this.setState({ n: this.state.n + 1 })\n  }\n  render() {\n    return \u003Cbutton onClick={this.handleClick}>{this.state.n}\u003C\u002Fbutton>\n  }\n}\n\n\u002F\u002F Option 3: arrow in render (new function per render)\n\u003Cbutton onClick={() => this.handleClick()}>\n```\n\n**Rule of thumb:** class fields (option 2) is the cleanest in modern JS.\nIn function components, this issue doesn't exist at all — another reason\nto prefer them.\n",{"id":1637,"difficulty":208,"q":1638,"a":1639},"throttle-debounce","How do you throttle or debounce an event handler in React?","Wrap the handler with a throttle\u002Fdebounce utility (lodash is most common)\nbut **memoize the wrapper** with `useRef` or `useCallback` so you get a\nstable function reference across renders.\n\n```jsx\nimport { useCallback, useRef } from 'react'\nimport debounce from 'lodash\u002Fdebounce'\n\nfunction SearchInput({ onSearch }) {\n  \u002F\u002F useRef keeps the debounced fn stable across renders\n  const debouncedSearch = useRef(\n    debounce((value) => onSearch(value), 300)\n  ).current\n\n  \u002F\u002F Clean up on unmount\n  useEffect(() => () => debouncedSearch.cancel(), [debouncedSearch])\n\n  return (\n    \u003Cinput onChange={e => debouncedSearch(e.target.value)} \u002F>\n  )\n}\n```\n\nCommon mistake: creating the debounced function inside the component body\nwithout memoization — a new function (with a fresh timer) is created each\nrender, so debouncing never actually fires.\n\n**Rule of thumb:** store debounced\u002Fthrottled functions in `useRef` (not\nrecreated on render) and cancel them on unmount to avoid calling stale\nclosures.\n",{"id":1641,"difficulty":121,"q":1642,"a":1643},"native-event","How do you access the native DOM event from a React SyntheticEvent?","Use `e.nativeEvent` to get the underlying browser `Event` object.\n\n```jsx\nfunction DragZone() {\n  function handleDrop(e) {\n    e.preventDefault()\n    \u002F\u002F SyntheticEvent doesn't expose dataTransfer directly in all React versions\n    const files = e.nativeEvent.dataTransfer?.files\n    console.log(files)\n  }\n  return \u003Cdiv onDrop={handleDrop} onDragOver={e => e.preventDefault()}>Drop here\u003C\u002Fdiv>\n}\n```\n\nReasons you might need `nativeEvent`:\n- Accessing properties not forwarded by the SyntheticEvent wrapper.\n- Passing the event to a non-React library that checks `instanceof Event`.\n- Calling browser-only APIs like `getCoalescedEvents()` for pointer events.\n\n**Rule of thumb:** start with the SyntheticEvent API; only reach for\n`nativeEvent` when you need a property or method that the wrapper doesn't\nexpose.\n",{"id":1645,"difficulty":121,"q":1646,"a":1647},"events-in-list","What is the most efficient way to handle events in a list of items?","Instead of passing a unique arrow function per item (which creates N\nclosures), use a single handler and identify the target via `data-*`\nattributes or `e.currentTarget`.\n\n```jsx\nfunction TodoList({ todos, onToggle }) {\n  function handleClick(e) {\n    const id = e.currentTarget.dataset.id\n    onToggle(id)\n  }\n\n  return (\n    \u003Cul>\n      {todos.map(todo => (\n        \u003Cli key={todo.id}>\n          \u003Cbutton data-id={todo.id} onClick={handleClick}>\n            {todo.text}\n          \u003C\u002Fbutton>\n        \u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n```\n\nFor most lists, arrow functions per item are fine — the overhead is\nnegligible. The `data-*` pattern is worth applying when the list is very\nlong or when the handlers are passed to `React.memo` children.\n\n**Rule of thumb:** profile before optimizing. Use `data-*` delegation when\nyou have hundreds of interactive rows and can measure the improvement.\n",{"id":1649,"difficulty":208,"q":1650,"a":1651},"synthetic-event-async","What happened to SyntheticEvent pooling and what changed in React 17?","Before React 17, React **pooled** SyntheticEvents to reduce garbage\ncollection pressure. After your handler returned, React would reset all\nproperties to `null` and return the event object to the pool.\n\n```jsx\n\u002F\u002F Pre-React 17 bug\nfunction handleChange(e) {\n  setTimeout(() => {\n    console.log(e.target.value)  \u002F\u002F CRASH — e.target is null after handler exits\n  }, 100)\n}\n\n\u002F\u002F Pre-React 17 fix: persist the event\nfunction handleChange(e) {\n  e.persist()  \u002F\u002F remove from pool, keep properties\n  setTimeout(() => {\n    console.log(e.target.value)  \u002F\u002F safe\n  }, 100)\n}\n```\n\n**React 17 removed event pooling.** SyntheticEvents are now regular objects\nthat are not recycled. Accessing them asynchronously is safe, and\n`e.persist()` is a no-op (still exists to avoid breaking old code).\n\n**Rule of thumb:** in React 17+ projects you don't need `e.persist()`. If\nyou see it in a codebase, it's either legacy code or a defensive habit from\nolder React.\n",17,{"description":94},"React event handling interview questions — SyntheticEvent, camelCase events, preventDefault, stopPropagation, passing arguments, controlled inputs, and event delegation.","react\u002Fcomponents\u002Fevent-handling","ouA3PzTAevet2voyeMiClKokS89VxQIssKu_33PPQ1o",{"id":1658,"title":1659,"body":1660,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1664,"navigation":100,"order":37,"path":1665,"questions":1666,"questionsCount":168,"related":169,"seo":1727,"seoDescription":1728,"stem":1729,"subtopic":1730,"topic":20,"topicSlug":21,"updated":174,"__hash__":1731},"qa\u002Freact\u002Fhooks\u002Fusecontext.md","Usecontext",{"type":91,"value":1661,"toc":1662},[],{"title":94,"searchDepth":11,"depth":11,"links":1663},[],{},"\u002Freact\u002Fhooks\u002Fusecontext",[1667,1671,1675,1679,1683,1686,1690,1694,1698,1702,1705,1709,1713,1716,1720,1724],{"id":1668,"difficulty":96,"q":1669,"a":1670},"what-is-usecontext","What does useContext do?","`useContext` reads the current value of a Context object and subscribes the\ncomponent to updates. When the nearest matching `\u003CProvider>` above it re-renders\nwith a new value, the consumer re-renders with that new value too.\n\n```jsx\nconst theme = useContext(ThemeContext)\n\u002F\u002F reads the value from \u003CThemeContext.Provider value={...}> above\n```\n\nIt replaces the older `\u003CThemeContext.Consumer>` render-prop API with a simple\nfunction call. The context must be created with `React.createContext` and the\ncomponent must be rendered inside the matching Provider.\n\n**Rule of thumb:** `useContext` solves \"prop drilling\" — passing data through many\nlayers of components that don't need it themselves.\n",{"id":1672,"difficulty":96,"q":1673,"a":1674},"create-context","How do you create and provide a context?","Call `createContext` with a default value (used when no Provider is found), then\nwrap the component tree with a `\u003CProvider>` and pass the current value.\n\n```jsx\n\u002F\u002F 1. create\nexport const ThemeContext = createContext('light')\n\n\u002F\u002F 2. provide\nfunction App() {\n  const [theme, setTheme] = useState('dark')\n  return (\n    \u003CThemeContext.Provider value={theme}>\n      \u003CPage \u002F>\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\n\u002F\u002F 3. consume\nfunction Button() {\n  const theme = useContext(ThemeContext)\n  return \u003Cbutton className={theme}>click\u003C\u002Fbutton>\n}\n```\n\nThe default value passed to `createContext` is only used when a component reads\nthe context without any Provider above it — useful for testing in isolation.\n",{"id":1676,"difficulty":121,"q":1677,"a":1678},"context-default-value","When is the createContext default value used?","The default value is used when a consumer has **no matching Provider** anywhere\nabove it in the tree. It does **not** apply when a Provider passes `undefined` or\n`null` as its `value` — those are valid values that override the default.\n\n```jsx\nconst Ctx = createContext('default')\n\n\u002F\u002F no Provider -> 'default'\nfunction App() {\n  return \u003CChild \u002F>\n}\n\n\u002F\u002F Provider with undefined -> consumer sees undefined, not 'default'\n\u003CCtx.Provider value={undefined}>\n  \u003CChild \u002F> {\u002F* sees undefined *\u002F}\n\u003C\u002FCtx.Provider>\n```\n\nProviding a realistic default value helps when testing components in isolation\nwithout wrapping them in a Provider.\n",{"id":1680,"difficulty":208,"q":1681,"a":1682},"context-rerenders","What triggers a context consumer to re-render?","Every component that calls `useContext(Ctx)` re-renders when the **value** prop on\nthe nearest matching `\u003CCtx.Provider>` changes — React compares by `Object.is`.\n\n```jsx\n\u002F\u002F new object every render -> every consumer re-renders even if data is identical\n\u003CUserContext.Provider value={{ user, setUser }}>\n  ...\n\u003C\u002FUserContext.Provider>\n\n\u002F\u002F stable object -> consumers only re-render when user actually changes\nconst ctxValue = useMemo(() => ({ user, setUser }), [user])\n\u003CUserContext.Provider value={ctxValue}>\n  ...\n\u003C\u002FUserContext.Provider>\n```\n\nThis is the #1 context performance trap. An object\u002Farray literal in the Provider's\nvalue is a new reference every render, causing all consumers to re-render even when\nthe data is unchanged. Memoize the value object to prevent this.\n",{"id":1373,"difficulty":121,"q":1684,"a":1685},"When should you use context instead of props?","Use context for data that is **globally shared across many components** at\ndifferent nesting levels where passing props becomes verbose: user auth, theme,\nlocale, feature flags. Avoid it for state that only flows two or three levels deep —\nprop drilling that shallow is cleaner and more explicit.\n\n```jsx\n\u002F\u002F fine as props: parent -> child -> grandchild (2 levels)\n\u003CModal title=\"Delete?\" onConfirm={handleDelete} \u002F>\n\n\u002F\u002F better as context: user object needed in nav, sidebar, avatar, settings\nconst { user } = useContext(AuthContext)\n```\n\nContext is not a performance tool — every consumer re-renders on value changes.\nIf you need selective subscriptions, reach for a state management library.\n",{"id":1687,"difficulty":121,"q":1688,"a":1689},"update-context","How do you update context from a consumer?","Include the setter function in the context value alongside the data. Consumers\ncall the setter, which lives in the Provider's parent and causes it to re-render\nwith the new value — which propagates to all consumers.\n\n```jsx\nconst ThemeContext = createContext(null)\n\nfunction ThemeProvider({ children }) {\n  const [theme, setTheme] = useState('dark')\n  return (\n    \u003CThemeContext.Provider value={{ theme, setTheme }}>\n      {children}\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nfunction Toggle() {\n  const { theme, setTheme } = useContext(ThemeContext)\n  return \u003Cbutton onClick={() => setTheme(t => t === 'dark' ? 'light' : 'dark')}>{theme}\u003C\u002Fbutton>\n}\n```\n\nThe setter has stable identity (it's a `useState` setter), so including it in the\nvalue doesn't cause extra renders — just make sure to memoize the value object.\n",{"id":1691,"difficulty":121,"q":1692,"a":1693},"multiple-contexts","Can a component read from multiple contexts?","Yes — call `useContext` once per context. There's no limit and each subscription\nis independent.\n\n```jsx\nfunction Dashboard() {\n  const { user } = useContext(AuthContext)\n  const { theme } = useContext(ThemeContext)\n  const { locale } = useContext(LocaleContext)\n  \u002F\u002F ...\n}\n```\n\nEach call registers a separate subscription. The component re-renders whenever\n*any* of the context values changes. If too many contexts cause unwanted renders,\nsplit responsibilities or memoize the component with `React.memo` (though context\nupdates bypass memo).\n",{"id":1695,"difficulty":208,"q":1696,"a":1697},"split-context","How do you avoid unnecessary re-renders caused by a large context?","Split the context by **change frequency**: separate read-rarely data (user profile)\nfrom changes-often data (notifications count). Consumers that only need stable\ndata won't re-render when the volatile part updates.\n\n```jsx\n\u002F\u002F one big context -> all consumers re-render when count changes\n\u003CAppContext.Provider value={{ user, notifCount }}>\n\n\u002F\u002F split by frequency\n\u003CUserContext.Provider value={user}>       {\u002F* changes rarely *\u002F}\n  \u003CNotifContext.Provider value={notifCount}>  {\u002F* changes often *\u002F}\n    \u003CApp \u002F>\n  \u003C\u002FNotifContext.Provider>\n\u003C\u002FUserContext.Provider>\n```\n\nComponents reading only `UserContext` are unaffected by notification updates.\nThis is the recommended pattern over a single monolithic context.\n",{"id":1699,"difficulty":208,"q":1700,"a":1701},"context-memo-bypass","Does React.memo protect a component from context re-renders?","No. `React.memo` skips re-renders when **props** don't change, but it has no\neffect on context updates — a memoized component still re-renders when a context\nvalue it reads changes.\n\n```jsx\nconst MemoButton = React.memo(function Button() {\n  const theme = useContext(ThemeContext) \u002F\u002F still re-renders on ThemeContext change\n  return \u003Cbutton className={theme}>click\u003C\u002Fbutton>\n})\n```\n\nTo avoid unnecessary renders, memoize the context **value** at the Provider level\n(so it doesn't change unnecessarily) rather than trying to memo the consumers.\n",{"id":1377,"difficulty":121,"q":1703,"a":1704},"When should you use context vs. a state management library?","**Context** suits simple, low-frequency global data: auth session, theme, locale.\nIt re-renders all consumers on any change and has no built-in selectors or\nmiddleware.\n\n**State management libraries** (Redux Toolkit, Zustand, Jotai) suit complex,\nfrequently-changing shared state where you need selective subscriptions (only\nre-render components that care about specific slices), devtools, or middleware\nlike logging and async thunks.\n\n```jsx\n\u002F\u002F context: good for this\nconst { user } = useContext(AuthContext)   \u002F\u002F stable, infrequent\n\n\u002F\u002F library: better for this\nconst cartCount = useSelector(state => state.cart.items.length) \u002F\u002F selective\n```\n\nThe context API is built in and zero-cost; a library adds a dependency but pays\nfor itself with high-churn data.\n",{"id":1706,"difficulty":96,"q":1707,"a":1708},"context-vs-consumer","How does useContext compare to the Context.Consumer component?","Both read the same context value, but `useContext` is simpler — you call it and\nget the value directly instead of rendering a component with a render-prop callback.\n\n```jsx\n\u002F\u002F old: Consumer render prop\n\u003CThemeContext.Consumer>\n  {theme => \u003Cbutton className={theme}>click\u003C\u002Fbutton>}\n\u003C\u002FThemeContext.Consumer>\n\n\u002F\u002F new: useContext\nconst theme = useContext(ThemeContext)\n```\n\n`useContext` also avoids the nesting (\"callback hell\") when consuming multiple\ncontexts. Functionally they are equivalent; `useContext` is the idiomatic modern\napproach.\n",{"id":1710,"difficulty":121,"q":1711,"a":1712},"context-provider-pattern","What is the context + provider custom hook pattern?","Encapsulate the context, Provider, and a custom hook in one file so consumers\nnever import the raw context object — they only call the hook. The hook also\nenforces that the component is inside the Provider.\n\n```jsx\nconst AuthContext = createContext(null)\n\nexport function AuthProvider({ children }) {\n  const [user, setUser] = useState(null)\n  return \u003CAuthContext.Provider value={{ user, setUser }}>{children}\u003C\u002FAuthContext.Provider>\n}\n\nexport function useAuth() {\n  const ctx = useContext(AuthContext)\n  if (!ctx) throw new Error('useAuth must be used within AuthProvider')\n  return ctx\n}\n```\n\nConsumers call `useAuth()` directly, which gives a clear error if they're\naccidentally rendered outside the Provider rather than silently getting `null`.\n",{"id":1405,"difficulty":121,"q":1714,"a":1715},"How do you test components that consume context?","Wrap the component under test in the Provider with a test value, or create a\nhelper wrapper function for your testing library.\n\n```jsx\nfunction renderWithAuth(ui, { user = testUser } = {}) {\n  return render(\n    \u003CAuthProvider initialUser={user}>{ui}\u003C\u002FAuthProvider>\n  )\n}\n\ntest('shows username', () => {\n  renderWithAuth(\u003CProfile \u002F>)\n  expect(screen.getByText('Ada')).toBeInTheDocument()\n})\n```\n\nThis is why the \"Provider + custom hook\" pattern is useful: you can also supply\na mock Provider in tests that returns controlled values without hitting real auth.\n",{"id":1717,"difficulty":208,"q":1718,"a":1719},"context-stale","Can context cause stale data bugs similar to useEffect closures?","Yes, if you capture a context value in a callback or effect and the context value\nlater changes, the closure holds the old value. The fix is the same: include the\ncontext value in the dependency array of effects and callbacks.\n\n```jsx\nconst { user } = useContext(AuthContext)\nuseEffect(() => {\n  log(user.id) \u002F\u002F safe: re-runs when user changes\n}, [user])\n\nconst handleClick = useCallback(() => {\n  post(user.id) \u002F\u002F safe: recreated when user changes\n}, [user])\n```\n\n`useContext` itself is always fresh (it reads the current value), but captured\nclosures aren't — always list context-derived values in your dependency arrays.\n",{"id":1721,"difficulty":208,"q":1722,"a":1723},"context-propagation-skip","Can you stop context from propagating to part of the tree?","Yes — render another Provider with a **different value** lower in the tree. All\nconsumers below it receive the new value instead of the one from above.\n\n```jsx\n\u003CThemeContext.Provider value=\"dark\">\n  \u003CHeader \u002F>  {\u002F* sees 'dark' *\u002F}\n  \u003CThemeContext.Provider value=\"light\">\n    \u003CSidebar \u002F>  {\u002F* sees 'light' *\u002F}\n  \u003C\u002FThemeContext.Provider>\n\u003C\u002FThemeContext.Provider>\n```\n\nThis is how libraries like `react-query` allow nested query clients. You cannot\n\"block\" a context — only override it with a new Provider further down.\n",{"id":1393,"difficulty":121,"q":1725,"a":1726},"How do you lazily initialize context state?","Pass a function to `useState` inside the Provider's component, just as you would\nfor any expensive initial computation.\n\n```jsx\nfunction AuthProvider({ children }) {\n  const [user, setUser] = useState(() => JSON.parse(localStorage.getItem('user')))\n  return (\n    \u003CAuthContext.Provider value={{ user, setUser }}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  )\n}\n```\n\nThe lazy initializer runs once, so the `localStorage` parse only happens on\nmount. This is especially useful for context that hydrates from storage or a\ncookie without re-parsing on every render.\n",{"description":94},"React useContext interview questions — creating and consuming context, avoiding unnecessary re-renders, splitting contexts, and context vs state management.","react\u002Fhooks\u002Fusecontext","useContext","uEm9a7m1liNgtTD-nuFSTPFcKLtu4BYjHpDNjkPPqmw",{"id":1733,"title":1734,"body":1735,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1739,"navigation":100,"order":37,"path":1740,"questions":1741,"questionsCount":589,"related":169,"seo":1822,"seoDescription":1823,"stem":1824,"subtopic":1734,"topic":72,"topicSlug":74,"updated":420,"__hash__":1825},"qa\u002Freact\u002Fpatterns\u002Ferror-boundaries.md","Error Boundaries",{"type":91,"value":1736,"toc":1737},[],{"title":94,"searchDepth":11,"depth":11,"links":1738},[],{},"\u002Freact\u002Fpatterns\u002Ferror-boundaries",[1742,1746,1750,1754,1758,1762,1766,1770,1774,1778,1782,1786,1790,1794,1798,1802,1806,1810,1814,1818],{"id":1743,"difficulty":96,"q":1744,"a":1745},"what-is-error-boundary","What is an error boundary and what problem does it solve in React?","An **error boundary** is a React class component that catches **JavaScript errors\nanywhere in its child component tree**, logs them, and renders a **fallback UI**\ninstead of crashing the whole application.\n\nBefore error boundaries existed, a single uncaught render error would unmount the\nentire React tree and leave users with a blank screen. Error boundaries provide\n**fault isolation** — only the subtree that threw is replaced with a fallback;\neverything outside the boundary keeps running normally.\n\n```jsx\n\u002F\u002F Without an error boundary, one bad render kills the whole app.\n\u002F\u002F With one, only the ErrorWidget section crashes — the rest survives.\n\u003CApp>\n  \u003CHeader \u002F>           {\u002F* still renders fine *\u002F}\n  \u003CErrorBoundary fallback={\u003Cp>Widget failed.\u003C\u002Fp>}>\n    \u003CErrorWidget \u002F>    {\u002F* throws → caught here, fallback shown *\u002F}\n  \u003C\u002FErrorBoundary>\n  \u003CFooter \u002F>           {\u002F* still renders fine *\u002F}\n\u003C\u002FApp>\n```\n\n**Rule of thumb:** An error boundary is a circuit breaker — it stops one bad\ncomponent from taking down the entire user interface.\n",{"id":1747,"difficulty":121,"q":1748,"a":1749},"getDerivedStateFromError","What does getDerivedStateFromError do and when is it called?","`getDerivedStateFromError` is a **static lifecycle method** called during the\n**render phase** when a descendant throws. It receives the thrown error and must\nreturn an object that is merged into the component's state — typically a flag\nthat switches the component from rendering children to rendering the fallback UI.\n\nBecause it runs during the render phase it must be **pure and side-effect free**.\nUse it only to update state; do all logging in `componentDidCatch` instead.\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false, error: null };\n\n  \u002F\u002F Called during render phase — update state to trigger fallback\n  static getDerivedStateFromError(error) {\n    return { hasError: true, error }; \u002F\u002F pure — no side effects here\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return \u003Cp>Something went wrong.\u003C\u002Fp>; \u002F\u002F fallback UI\n    }\n    return this.props.children; \u002F\u002F normal path\n  }\n}\n```\n\n**Rule of thumb:** `getDerivedStateFromError` answers *what* to show; it must\nstay pure — keep it to one `return` statement that updates state.\n",{"id":1751,"difficulty":121,"q":1752,"a":1753},"componentDidCatch","What is componentDidCatch and how does it differ from getDerivedStateFromError?","`componentDidCatch` is called during the **commit phase**, after the fallback UI\nhas been painted. Unlike `getDerivedStateFromError` it receives both the **error**\nand an **info object** (`{ componentStack }`) that contains the component stack\ntrace. It is the right place for **side effects** such as logging to an error\nmonitoring service.\n\nThe two methods are complementary: `getDerivedStateFromError` flips the state to\nshow the fallback; `componentDidCatch` does the logging.\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n\n  static getDerivedStateFromError() {\n    return { hasError: true }; \u002F\u002F render phase — only flip the flag\n  }\n\n  componentDidCatch(error, info) {\n    \u002F\u002F Commit phase — side effects are safe here\n    \u002F\u002F info.componentStack is the React component call stack\n    logErrorToSentry(error, { extra: { componentStack: info.componentStack } });\n  }\n\n  render() {\n    return this.state.hasError\n      ? \u003CFallbackUI \u002F>\n      : this.props.children;\n  }\n}\n```\n\n**Rule of thumb:** `getDerivedStateFromError` = pure state update (render phase);\n`componentDidCatch` = side effects like logging (commit phase). Use both together.\n",{"id":1755,"difficulty":121,"q":1756,"a":1757},"why-class-component","Why must error boundaries be class components? Is there a hook-based alternative?","Error boundaries require `getDerivedStateFromError` and `componentDidCatch`, which\nare **class lifecycle methods** with no hook equivalents in React's public API. The\nReact team has acknowledged this gap but has not shipped a functional replacement —\nas of React 18 you still need a class component.\n\nThe `react-error-boundary` library wraps a class boundary behind a friendly\nfunctional API, which is the recommended way to avoid writing class components\nyourself in modern codebases.\n\n```jsx\n\u002F\u002F The class is unavoidable at the boundary itself…\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n  static getDerivedStateFromError() { return { hasError: true }; }\n  render() {\n    return this.state.hasError\n      ? this.props.fallback\n      : this.props.children;\n  }\n}\n\n\u002F\u002F …but you can wrap it so callers use a clean functional component:\nfunction SafeWidget({ children }) {\n  return (\n    \u003CErrorBoundary fallback={\u003Cp>Widget failed\u003C\u002Fp>}>\n      {children}\n    \u003C\u002FErrorBoundary>\n  );\n}\n```\n\n**Rule of thumb:** You need a class somewhere in the chain — use\n`react-error-boundary` to hide it so the rest of your codebase stays functional.\n",{"id":1759,"difficulty":121,"q":1760,"a":1761},"errors-not-caught","What types of errors are NOT caught by error boundaries?","Error boundaries catch errors that occur during **rendering, lifecycle methods, and\nconstructors of class components** in the tree below them. They do NOT catch:\n\n- **Event handlers** — React does not call event handlers during rendering; wrap\n  them in a regular `try\u002Fcatch` instead.\n- **Asynchronous code** — errors in `setTimeout`, Promises, or `async\u002Fawait` are\n  not caught because they happen outside React's call stack.\n- **Server-side rendering** — boundaries only work client-side.\n- **Errors thrown inside the boundary itself** — a boundary cannot catch its own\n  render errors.\n\n```jsx\nfunction BadButton() {\n  const handleClick = () => {\n    try {\n      riskyOperation(); \u002F\u002F must use try\u002Fcatch — boundary won't catch this\n    } catch (err) {\n      reportError(err);\n    }\n  };\n  return \u003Cbutton onClick={handleClick}>Go\u003C\u002Fbutton>;\n}\n\nasync function fetchData() {\n  \u002F\u002F Promise rejection — NOT caught by error boundary\n  \u002F\u002F Use .catch() or try\u002Fcatch inside useEffect instead\n  const data = await api.get('\u002Fthings');\n}\n```\n\n**Rule of thumb:** If the error happens outside a React render call — in a\ncallback or a Promise — an error boundary will not catch it.\n",{"id":1763,"difficulty":121,"q":1764,"a":1765},"error-boundary-granularity","At what granularity should you place error boundaries in an application?","**Coarser boundaries** protect large sections but may hide too much on failure.\n**Finer boundaries** give surgical fallbacks but add boilerplate. A typical\nthree-level strategy works well:\n\n1. **App\u002Froot level** — one top-level boundary as a last resort fallback (blank\n   screen prevention).\n2. **Route\u002Fpage level** — a boundary per route so a broken page doesn't kill\n   navigation.\n3. **Widget\u002Ffeature level** — boundaries around independent sidebar panels,\n   recommendation carousels, or ad slots that can fail without affecting the\n   main content.\n\n```jsx\n\u003CRootErrorBoundary>        {\u002F* level 1 — last resort *\u002F}\n  \u003CRouter>\n    \u003CRoutes>\n      \u003CRoute                 \u002F* level 2 — per route *\u002F\n        element={\n          \u003CRouteErrorBoundary>\n            \u003CDashboardPage \u002F>\n          \u003C\u002FRouteErrorBoundary>\n        }\n      \u002F>\n    \u003C\u002FRoutes>\n    \u003CSidebar>\n      \u003CWidgetErrorBoundary> {\u002F* level 3 — isolated widget *\u002F}\n        \u003CRecommendationsPanel \u002F>\n      \u003C\u002FWidgetErrorBoundary>\n    \u003C\u002FSidebar>\n  \u003C\u002FRouter>\n\u003C\u002FRootErrorBoundary>\n```\n\n**Rule of thumb:** Place a boundary wherever you can write a meaningful, scoped\nfallback message — the finer the boundary, the better the user experience.\n",{"id":1767,"difficulty":96,"q":1768,"a":1769},"fallback-ui-design","What should a good fallback UI include?","A well-designed fallback UI tells users **what happened**, **what they can do\nnext**, and ideally provides a **recovery action** (reload, retry, go home).\nAvoid exposing raw error messages or stack traces in production.\n\nGood fallback UIs are:\n- **Scoped** — show only in the crashed section, not full-page unless necessary.\n- **Actionable** — include a retry button or a link to the home page.\n- **Branded** — styled consistently, not a plain white box.\n- **Non-alarming** — phrase errors in friendly, non-technical language.\n\n```jsx\nfunction FallbackUI({ error, resetErrorBoundary }) {\n  return (\n    \u003Cdiv role=\"alert\" className=\"error-panel\">\n      \u003Ch2>This section couldn't load.\u003C\u002Fh2>\n      {\u002F* Never show error.message to users in production *\u002F}\n      \u003Cp>Try refreshing or come back later.\u003C\u002Fp>\n      \u003Cbutton onClick={resetErrorBoundary}>Try again\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** A fallback UI should answer \"what broke and what can I do?\" —\nnever dump a raw error message onto users.\n",{"id":1771,"difficulty":121,"q":1772,"a":1773},"error-recovery-reset","How do you implement error recovery — letting the user retry after a crash?","Recovery works by **resetting the boundary's error state** so it re-renders its\nchildren on the next render. In a hand-rolled boundary, expose a `reset` method\nor pass a `onReset` callback. In `react-error-boundary`, use the\n`resetErrorBoundary` prop injected into the fallback component.\n\nThe key subtlety: if the component that threw hasn't changed, it will throw again\nimmediately. Pair the reset with a **key change** or a data refetch so the\nre-render has a chance to succeed.\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n\n  static getDerivedStateFromError() { return { hasError: true }; }\n\n  \u002F\u002F Expose reset so parent or fallback can call it\n  reset = () => this.setState({ hasError: false });\n\n  render() {\n    if (this.state.hasError) {\n      return (\n        \u003CFallbackUI onRetry={this.reset} \u002F>  \u002F\u002F pass reset down\n      );\n    }\n    return this.props.children;\n  }\n}\n```\n\n**Rule of thumb:** Always give users a retry path — but also make sure the retry\nactually fixes the underlying condition, not just re-throws the same error.\n",{"id":1775,"difficulty":121,"q":1776,"a":1777},"react-error-boundary-library","What does the react-error-boundary library provide over a hand-rolled boundary?","`react-error-boundary` is the community-standard wrapper that adds four key\nfeatures on top of the raw class API:\n\n1. **`\u003CErrorBoundary>`** — a ready-made class boundary that accepts `fallback`,\n   `FallbackComponent`, `onError`, `onReset`, and `resetKeys` props.\n2. **`FallbackComponent`** — receives `{ error, resetErrorBoundary }` so the\n   fallback can trigger recovery without prop drilling.\n3. **`resetKeys`** — an array of values; when any changes the boundary\n   auto-resets, enabling data-driven recovery.\n4. **`useErrorBoundary()`** — hook that lets any functional component throw\n   errors up to the nearest boundary programmatically (useful for async errors).\n\n```jsx\nimport { ErrorBoundary } from 'react-error-boundary';\n\nfunction Fallback({ error, resetErrorBoundary }) {\n  return (\n    \u003Cdiv role=\"alert\">\n      \u003Cp>Error: {error.message}\u003C\u002Fp>\n      \u003Cbutton onClick={resetErrorBoundary}>Retry\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  );\n}\n\n\u002F\u002F resetKeys: boundary auto-resets whenever `userId` changes\n\u003CErrorBoundary FallbackComponent={Fallback} resetKeys={[userId]}>\n  \u003CUserProfile userId={userId} \u002F>\n\u003C\u002FErrorBoundary>\n```\n\n**Rule of thumb:** Use `react-error-boundary` in production — it removes the need\nto write class components and handles the reset logic correctly.\n",{"id":1779,"difficulty":121,"q":1780,"a":1781},"useErrorBoundary-hook","How does useErrorBoundary let functional components interact with an error boundary?","`useErrorBoundary` (from `react-error-boundary`) returns\n`{ showBoundary, resetBoundary }`. Calling `showBoundary(error)` throws the error\nup to the nearest `\u003CErrorBoundary>` as if it had been thrown during rendering.\nThis is the standard way to route **async and event-handler errors** through a\nboundary, since those errors are not caught automatically.\n\n```jsx\nimport { useErrorBoundary } from 'react-error-boundary';\n\nfunction UserList() {\n  const { showBoundary } = useErrorBoundary();\n\n  useEffect(() => {\n    fetchUsers()\n      .then(setUsers)\n      .catch((err) => {\n        showBoundary(err); \u002F\u002F manually escalate to nearest boundary\n      });\n  }, []);\n\n  return \u003Cul>{\u002F* ... *\u002F}\u003C\u002Ful>;\n}\n```\n\n**Rule of thumb:** Use `showBoundary` from `useErrorBoundary` any time you need\nto route a non-render error (async, event handler) into the boundary system.\n",{"id":1783,"difficulty":121,"q":1784,"a":1785},"resetKeys-prop","What is the resetKeys prop in react-error-boundary and when should you use it?","`resetKeys` is an array of values passed to `\u003CErrorBoundary>`. Whenever any value\nin the array **changes between renders**, the boundary automatically resets its\nerror state and re-renders its children. This enables **data-driven recovery**\nwithout requiring the user to click a retry button.\n\nCommon use cases: resetting when the user navigates to a different resource\n(changing `userId`, `postId`), or when a query key changes after a refetch.\n\n```jsx\n\u002F\u002F Automatically retry when the user switches to a different profile\n\u003CErrorBoundary\n  FallbackComponent={ProfileError}\n  resetKeys={[userId]}       \u002F\u002F boundary resets whenever userId changes\n  onReset={() => refetch()}  \u002F\u002F also trigger a fresh network request\n>\n  \u003CUserProfile userId={userId} \u002F>\n\u003C\u002FErrorBoundary>\n```\n\n**Rule of thumb:** Use `resetKeys` when navigating between resources — it gives\nevery new resource a clean slate without manual reset button clicks.\n",{"id":1787,"difficulty":121,"q":1788,"a":1789},"logging-to-sentry","How do you log errors to an external service like Sentry inside componentDidCatch?","`componentDidCatch` is the correct hook for external logging because it runs\nduring the **commit phase** where side effects are safe. It receives both the\n`error` and `info.componentStack`, which together give a useful trace for\ndebugging. Pass both to your monitoring SDK.\n\nWith `react-error-boundary`, use the `onError` prop instead of subclassing.\n\n```jsx\nimport * as Sentry from '@sentry\u002Freact';\n\n\u002F\u002F Option 1: hand-rolled class boundary\ncomponentDidCatch(error, info) {\n  Sentry.captureException(error, {\n    extra: { componentStack: info.componentStack },\n  });\n}\n\n\u002F\u002F Option 2: react-error-boundary — onError prop (preferred)\n\u003CErrorBoundary\n  FallbackComponent={Fallback}\n  onError={(error, info) => {\n    Sentry.captureException(error, {\n      extra: { componentStack: info.componentStack },\n    });\n  }}\n>\n  \u003CApp \u002F>\n\u003C\u002FErrorBoundary>\n```\n\n**Rule of thumb:** Always log both the error *and* the component stack —\n`error.message` tells you what crashed; `componentStack` tells you where.\n",{"id":1791,"difficulty":208,"q":1792,"a":1793},"error-boundary-with-suspense","How do error boundaries work alongside React Suspense?","`\u003CSuspense>` handles **loading states** (a component \"suspends\" by throwing a\nPromise); error boundaries handle **error states** (a component throws an Error).\nThe two are designed to compose: wrap a `\u003CSuspense>` inside an `\u003CErrorBoundary>`\nto cover both loading and failure for the same subtree.\n\nIn concurrent mode, React can retry suspended trees. If a retry results in an\nError (not a Promise), the nearest error boundary catches it.\n\n```jsx\n\u002F\u002F Layered: error boundary wraps Suspense — covers both failure modes\n\u003CErrorBoundary FallbackComponent={ErrorFallback}>\n  \u003CSuspense fallback={\u003CSpinner \u002F>}>\n    {\u002F* LazyComponent suspends while loading → shows Spinner     *\u002F}\n    {\u002F* If it throws an Error instead → ErrorBoundary catches it *\u002F}\n    \u003CLazyComponent \u002F>\n  \u003C\u002FSuspense>\n\u003C\u002FErrorBoundary>\n```\n\n**Rule of thumb:** Wrap every `\u003CSuspense>` in an `\u003CErrorBoundary>` — Suspense\nonly handles the \"pending\" state; errors still need a boundary.\n",{"id":1795,"difficulty":208,"q":1796,"a":1797},"boundary-in-concurrent-mode","Does concurrent mode (React 18) change how error boundaries behave?","Concurrent React introduces **partial rendering** and **retries**, which changes\nsome boundary behavior:\n\n- React may attempt to render a subtree **multiple times** before committing, so\n  `getDerivedStateFromError` might be called more than once for the same error.\n  The method must remain pure.\n- With `startTransition`, React can **defer** a failing render and keep the\n  previous UI visible rather than immediately showing the boundary fallback,\n  giving a smoother degradation.\n- In **Strict Mode (development only)** React double-invokes render to surface\n  impure code — `componentDidCatch` is *not* double-invoked, but\n  `getDerivedStateFromError` may be.\n\n```jsx\n\u002F\u002F Wrap a non-urgent update in a transition:\n\u002F\u002F if it errors, React keeps showing the stale UI instead of the fallback\nconst [isPending, startTransition] = useTransition();\n\nstartTransition(() => {\n  setResourceId(newId); \u002F\u002F error here → boundary catches, but only after transition\n});\n```\n\n**Rule of thumb:** Keep `getDerivedStateFromError` pure — concurrent mode may\ncall it multiple times before a single commit.\n",{"id":1799,"difficulty":208,"q":1800,"a":1801},"error-boundary-itself-throws","What happens if an error boundary's own render method throws?","A boundary **cannot catch its own errors**. If `render()` or any lifecycle in the\nboundary class itself throws, React walks up the tree and looks for the next\nancestor error boundary. If none exists, the entire React tree is unmounted.\n\nThis means every error boundary implicitly relies on its parent being error-free —\nor being wrapped in another boundary. In practice, nest a minimal top-level\nboundary (that itself contains no complex logic) as the outermost root to catch\nanything a deeper boundary misses.\n\n```jsx\n\u002F\u002F A boundary that itself might fail — bad practice:\nclass RiskyBoundary extends React.Component {\n  static getDerivedStateFromError() { return { hasError: true }; }\n  render() {\n    if (this.state.hasError) {\n      return \u003CComplexFallbackThatMightAlsoThrow \u002F>; \u002F\u002F dangerous!\n    }\n    return this.props.children;\n  }\n}\n\n\u002F\u002F Keep fallback UI extremely simple — plain HTML, no data fetching:\nrender() {\n  if (this.state.hasError) {\n    return \u003Cp>Something went wrong.\u003C\u002Fp>; \u002F\u002F safe, cannot throw\n  }\n  return this.props.children;\n}\n```\n\n**Rule of thumb:** Keep fallback UI dead simple — it should be plain HTML or a\nstatic string, never something that fetches data or renders lazily.\n",{"id":1803,"difficulty":121,"q":1804,"a":1805},"testing-error-boundaries","How do you test an error boundary with React Testing Library?","The standard approach is to render a component that **intentionally throws** and\nassert that the fallback UI appears. React Testing Library does not suppress the\nconsole error from React's internal boundary reporting, so you should spy on\n`console.error` and suppress it to keep test output clean.\n\n```jsx\nimport { render, screen } from '@testing-library\u002Freact';\nimport { ErrorBoundary } from 'react-error-boundary';\n\n\u002F\u002F A component that always throws\nfunction Bomb() {\n  throw new Error('💥 test error');\n}\n\ntest('renders fallback when child throws', () => {\n  \u002F\u002F Suppress React's console.error noise in test output\n  const spy = jest.spyOn(console, 'error').mockImplementation(() => {});\n\n  render(\n    \u003CErrorBoundary fallback={\u003Cp>Boundary caught it\u003C\u002Fp>}>\n      \u003CBomb \u002F>\n    \u003C\u002FErrorBoundary>\n  );\n\n  expect(screen.getByText('Boundary caught it')).toBeInTheDocument();\n  spy.mockRestore(); \u002F\u002F always restore after test\n});\n```\n\n**Rule of thumb:** Suppress `console.error` during boundary tests to keep output\nclean, but always restore the spy — suppressing it globally hides real issues.\n",{"id":1807,"difficulty":121,"q":1808,"a":1809},"testing-reset-behavior","How do you test the reset\u002Fretry behavior of an error boundary?","Test recovery by simulating the retry action (clicking the button) and asserting\nthat the children render successfully after the error condition is cleared. Use\na **stateful wrapper** to toggle whether the child throws, simulating a data fix.\n\n```jsx\nlet shouldThrow = true;\n\nfunction MaybeThrow() {\n  if (shouldThrow) throw new Error('temporary failure');\n  return \u003Cp>Content loaded\u003C\u002Fp>;\n}\n\ntest('retries successfully after reset', async () => {\n  const spy = jest.spyOn(console, 'error').mockImplementation(() => {});\n\n  render(\n    \u003CErrorBoundary FallbackComponent={({ resetErrorBoundary }) => (\n      \u003Cbutton onClick={() => {\n        shouldThrow = false;     \u002F\u002F fix the condition before reset\n        resetErrorBoundary();    \u002F\u002F trigger boundary reset\n      }}>Retry\u003C\u002Fbutton>\n    )}>\n      \u003CMaybeThrow \u002F>\n    \u003C\u002FErrorBoundary>\n  );\n\n  await userEvent.click(screen.getByText('Retry'));\n  expect(screen.getByText('Content loaded')).toBeInTheDocument();\n  spy.mockRestore();\n});\n```\n\n**Rule of thumb:** Fix the error condition *before* calling `resetErrorBoundary`\n— otherwise the boundary resets and immediately throws again.\n",{"id":1811,"difficulty":96,"q":1812,"a":1813},"error-boundary-vs-try-catch","When should you use an error boundary vs a try\u002Fcatch block in React code?","**Error boundaries** protect against uncaught errors in the **render cycle** —\nthey intercept errors in `render`, lifecycle methods, and constructors. They\nprovide a declarative, component-scoped safety net.\n\n**try\u002Fcatch** is for **imperative code** — event handlers, async operations,\nand utility functions outside the React render cycle where boundaries cannot reach.\n\n| Scenario | Tool |\n|---|---|\n| Component throws during render | Error boundary |\n| `async` fetch in `useEffect` | try\u002Fcatch + state |\n| Button click handler throws | try\u002Fcatch |\n| Lazy-loaded component fails | Error boundary + Suspense |\n\n```jsx\n\u002F\u002F Render error → boundary handles it automatically\nfunction BadRender() {\n  const data = JSON.parse(null); \u002F\u002F throws during render → boundary catches\n  return \u003Cdiv>{data.name}\u003C\u002Fdiv>;\n}\n\n\u002F\u002F Async error → try\u002Fcatch needed\nasync function loadUser(id) {\n  try {\n    return await api.getUser(id);\n  } catch (err) {\n    showBoundary(err); \u002F\u002F escalate manually via useErrorBoundary\n  }\n}\n```\n\n**Rule of thumb:** Use boundaries for render errors, try\u002Fcatch for everything\nelse — then bridge async failures into boundaries via `showBoundary`.\n",{"id":1815,"difficulty":96,"q":1816,"a":1817},"multiple-boundaries-strategy","Can you have multiple error boundaries in the same component tree? How should they be nested?","Yes — React will propagate an unhandled error **up the tree** until it finds the\nnearest ancestor error boundary. Multiple nested boundaries each act as\nindependent catch points for their own subtrees.\n\nA healthy app has **multiple boundaries at different levels**:\n\n- The root boundary is the final backstop.\n- Route-level boundaries isolate page failures.\n- Widget-level boundaries isolate autonomous sections.\n\n```jsx\n\u002F\u002F Each boundary is independent — inner boundary failing falls up to outer\n\u003CAppBoundary>          {\u002F* outer: catches everything the inner misses *\u002F}\n  \u003CLayout>\n    \u003CNavBoundary>      {\u002F* isolates nav errors *\u002F}\n      \u003CNavigation \u002F>\n    \u003C\u002FNavBoundary>\n\n    \u003CPageBoundary>     {\u002F* isolates current page errors *\u002F}\n      \u003CMainContent>\n        \u003CWidgetBoundary> {\u002F* isolates a single widget *\u002F}\n          \u003CLiveFeed \u002F>\n        \u003C\u002FWidgetBoundary>\n      \u003C\u002FMainContent>\n    \u003C\u002FPageBoundary>\n  \u003C\u002FLayout>\n\u003C\u002FAppBoundary>\n```\n\n**Rule of thumb:** Think of boundaries like `try\u002Fcatch` nesting — the innermost\nmatching boundary catches the error; outer ones only fire if the inner one itself\nfails or doesn't exist.\n",{"id":1819,"difficulty":96,"q":1820,"a":1821},"production-vs-development","Why do error boundaries behave differently in development vs production?","In **development** (`react-dom\u002Fdevelopment`), React re-throws errors after calling\nthe boundary so that the browser's DevTools overlay and the console both show the\nfull stack trace. This means you'll see the error in the console **even when a\nboundary catches it** — that's intentional, not a bug.\n\nIn **production** (`react-dom\u002Fproduction`), the error is caught silently by the\nboundary, the fallback UI is shown, and no overlay appears. Users see only the\nfallback; developers see nothing unless logging (Sentry, etc.) is set up.\n\n```jsx\n\u002F\u002F Dev: React error overlay appears + console.error fires even with a boundary.\n\u002F\u002F This tells developers the error happened; the boundary still works in prod.\n\n\u002F\u002F A common mistake: developers test boundaries in dev, see the overlay,\n\u002F\u002F and think the boundary isn't working — it is; the overlay is just dev behavior.\n\ncomponentDidCatch(error, info) {\n  \u002F\u002F This is your ONLY visibility into boundary-caught errors in production\n  logToMonitoringService(error, info);\n}\n```\n\n**Rule of thumb:** The dev error overlay does not mean the boundary failed —\nReact shows it deliberately; always test production behavior with `npm run build`.\n",{"description":94},"React error boundary interview questions — componentDidCatch, getDerivedStateFromError, fallback UI, error recovery, async errors, and react-error-boundary library.","react\u002Fpatterns\u002Ferror-boundaries","HclcDy5UUQv4iAGr42ulavhLi8--jWKoMfQiQ1_LMnU",{"id":1827,"title":1828,"body":1829,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1833,"navigation":100,"order":37,"path":1835,"questions":1836,"questionsCount":493,"related":169,"seo":1897,"seoDescription":1898,"stem":1899,"subtopic":1900,"topic":45,"topicSlug":47,"updated":420,"__hash__":1901},"qa\u002Freact\u002Frendering-and-performance\u002Fusememo-usecallback-patterns.md","Usememo Usecallback Patterns",{"type":91,"value":1830,"toc":1831},[],{"title":94,"searchDepth":11,"depth":11,"links":1832},[],{"subtopicSlug":1834},"usememo-usecallback-patterns","\u002Freact\u002Frendering-and-performance\u002Fusememo-usecallback-patterns",[1837,1841,1845,1849,1853,1857,1861,1865,1869,1873,1877,1881,1885,1889,1893],{"id":1838,"difficulty":96,"q":1839,"a":1840},"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":1842,"difficulty":96,"q":1843,"a":1844},"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":1846,"difficulty":96,"q":1847,"a":1848},"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":1850,"difficulty":121,"q":1851,"a":1852},"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":1854,"difficulty":121,"q":1855,"a":1856},"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":1858,"difficulty":121,"q":1859,"a":1860},"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":1862,"difficulty":121,"q":1863,"a":1864},"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":1866,"difficulty":208,"q":1867,"a":1868},"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":1870,"difficulty":121,"q":1871,"a":1872},"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":1874,"difficulty":121,"q":1875,"a":1876},"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":1878,"difficulty":208,"q":1879,"a":1880},"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":1882,"difficulty":208,"q":1883,"a":1884},"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":1886,"difficulty":208,"q":1887,"a":1888},"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":1890,"difficulty":208,"q":1891,"a":1892},"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":1894,"difficulty":121,"q":1895,"a":1896},"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":94},"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":1903,"title":1904,"body":1905,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":1909,"navigation":100,"order":37,"path":1910,"questions":1911,"questionsCount":589,"related":169,"seo":1991,"seoDescription":1992,"stem":1993,"subtopic":1904,"topic":54,"topicSlug":56,"updated":420,"__hash__":1994},"qa\u002Freact\u002Frouting\u002Fnavigation-hooks.md","Navigation Hooks",{"type":91,"value":1906,"toc":1907},[],{"title":94,"searchDepth":11,"depth":11,"links":1908},[],{},"\u002Freact\u002Frouting\u002Fnavigation-hooks",[1912,1916,1919,1923,1927,1931,1935,1939,1943,1947,1951,1955,1959,1963,1967,1971,1975,1979,1983,1987],{"id":1913,"difficulty":96,"q":1914,"a":1915},"use-navigate-basic","What does useNavigate return and how do you use it for programmatic navigation?","**useNavigate** returns a **navigate function** that lets you redirect the user programmatically — typically inside event handlers, effects, or after async operations. Calling `navigate('\u002Fpath')` performs a **push** (adds a history entry); passing `{ replace: true }` performs a **replace** (overwrites the current entry).\n\n```jsx\nimport { useNavigate } from 'react-router-dom';\n\nfunction LoginForm() {\n  const navigate = useNavigate();\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    await login(credentials);\n    \u002F\u002F Push a new entry so the user can press Back to return to login\n    navigate('\u002Fdashboard');\n    \u002F\u002F Or replace so pressing Back does NOT return to this form:\n    \u002F\u002F navigate('\u002Fdashboard', { replace: true });\n  }\n\n  return \u003Cform onSubmit={handleSubmit}>...\u003C\u002Fform>;\n}\n```\n\n**Rule of thumb:** Use `replace: true` after authentication or any flow where letting the user navigate back to the previous page would be confusing or insecure.\n",{"id":578,"difficulty":96,"q":1917,"a":1918},"What is the difference between navigate('\u002Fpath') and navigate('\u002Fpath', { replace: true })?","Without options, **navigate('\u002Fpath')** is a **push**: it appends a new entry to the history stack, so pressing the browser Back button returns to the previous URL. With `{ replace: true }` it is a **replace**: the current history entry is overwritten and the previous URL is no longer reachable via Back.\n\n```jsx\nfunction CheckoutSuccess() {\n  const navigate = useNavigate();\n\n  useEffect(() => {\n    \u002F\u002F Replace so users can't press Back and accidentally re-submit the order\n    navigate('\u002Forder-confirmation', { replace: true });\n  }, [navigate]);\n\n  return null;\n}\n```\n\n**Rule of thumb:** Prefer `replace` for redirects that follow a destructive or one-way action (form submit, logout, payment confirmation); prefer the default push for normal link-like navigation.\n",{"id":1920,"difficulty":96,"q":1921,"a":1922},"navigate-back","How do you navigate back one step in history with useNavigate?","Pass the **integer `-1`** to `navigate` to go back one entry, `-2` for two entries, or `+1` to go forward. This mirrors the `window.history.go(delta)` API.\n\n```jsx\nfunction BackButton() {\n  const navigate = useNavigate();\n\n  return (\n    \u002F\u002F navigate(-1) pops the history stack by one entry\n    \u003Cbutton onClick={() => navigate(-1)}>← Back\u003C\u002Fbutton>\n  );\n}\n```\n\n**Rule of thumb:** Use `navigate(-1)` instead of `window.history.back()` so React Router stays in control of the transition; `window.history.back()` bypasses React Router's transition listeners and blockers.\n",{"id":1924,"difficulty":121,"q":1925,"a":1926},"navigate-state-passing","How do you pass state when navigating with useNavigate and how do you read it on the destination page?","Pass a **state** key in the options object. The state is stored in the history entry and is accessible on the destination via `useLocation().state`. It is **not** reflected in the URL so it disappears on a hard refresh.\n\n```jsx\n\u002F\u002F Source page\nfunction ProductCard({ product }) {\n  const navigate = useNavigate();\n  return (\n    \u003Cbutton onClick={() => navigate('\u002Fcheckout', { state: { productId: product.id } })}>\n      Buy now\n    \u003C\u002Fbutton>\n  );\n}\n\n\u002F\u002F Destination page\nimport { useLocation } from 'react-router-dom';\n\nfunction CheckoutPage() {\n  const { state } = useLocation();\n  \u002F\u002F state.productId is available here; undefined after a hard refresh\n  return \u003Cp>Checking out product {state?.productId}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Use location state for transient UI hints (e.g., \"came from search results\") — never for critical data that must survive a refresh; persist that in a store or URL param instead.\n",{"id":1928,"difficulty":96,"q":1929,"a":1930},"use-params-basic","What does useParams return and when would you use it?","**useParams** returns an **object whose keys match the dynamic segments** declared in the route path (prefixed with `:`). Use it whenever a component needs to read a URL parameter like an ID or slug.\n\n```jsx\n\u002F\u002F Route definition\n\u002F\u002F \u003CRoute path=\"\u002Fusers\u002F:userId\u002Fposts\u002F:postId\" element={\u003CPostDetail \u002F>} \u002F>\n\nfunction PostDetail() {\n  const { userId, postId } = useParams();\n  \u002F\u002F Both values are strings — always parse numbers before comparing\n  const numericPostId = Number(postId);\n\n  return \u003Cp>User {userId}, Post {numericPostId}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Always treat `useParams` values as **strings** and validate\u002Fparse them before use — a missing or malformed param will be `undefined`, not `null`.\n",{"id":1932,"difficulty":96,"q":1933,"a":1934},"use-location-fields","What fields does useLocation return and what is each one used for?","**useLocation** returns the current **location object** with five fields: `pathname` (the path string), `search` (the raw query string including `?`), `hash` (the fragment including `#`), `state` (arbitrary data attached by navigate), and `key` (a unique string per history entry).\n\n```jsx\nimport { useLocation } from 'react-router-dom';\n\nfunction DebugLocation() {\n  const location = useLocation();\n  \u002F\u002F location.pathname  → '\u002Fproducts\u002F42'\n  \u002F\u002F location.search    → '?sort=price&order=asc'\n  \u002F\u002F location.hash      → '#reviews'\n  \u002F\u002F location.state     → { from: '\u002Fcart' }  (if set by navigate)\n  \u002F\u002F location.key       → 'default' | 'abc123'\n\n  return \u003Cpre>{JSON.stringify(location, null, 2)}\u003C\u002Fpre>;\n}\n```\n\n**Rule of thumb:** Use `useLocation` to react to URL changes in side effects — put `location` in a `useEffect` dependency array to re-run whenever the user navigates.\n",{"id":1936,"difficulty":121,"q":1937,"a":1938},"use-search-params-read","How do you read query string parameters with useSearchParams?","**useSearchParams** returns a `[searchParams, setSearchParams]` tuple where `searchParams` is a **URLSearchParams** instance. Call `.get('key')` for a single value or `.getAll('key')` for repeated values.\n\n```jsx\nimport { useSearchParams } from 'react-router-dom';\n\nfunction ProductList() {\n  const [searchParams] = useSearchParams();\n\n  const category = searchParams.get('category'); \u002F\u002F 'electronics' | null\n  const page = Number(searchParams.get('page') ?? '1'); \u002F\u002F default to 1\n  const tags = searchParams.getAll('tag'); \u002F\u002F ['sale', 'new']\n\n  return \u003Cp>Showing {category} — page {page}\u003C\u002Fp>;\n}\n```\n\n**Rule of thumb:** Always provide a fallback when calling `.get()` — it returns `null` if the parameter is absent, which will cause bugs if you use it as a number or array without checking.\n",{"id":1940,"difficulty":121,"q":1941,"a":1942},"use-search-params-write","How do you update query parameters with useSearchParams without losing existing ones?","Call **setSearchParams** with a callback to get the current params and mutate a copy, or pass a plain object to replace all params. Passing the function form is safer when you only want to update a subset.\n\n```jsx\nfunction SortControl() {\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  function handleSort(field) {\n    setSearchParams(prev => {\n      \u002F\u002F Clone so we don't mutate the live object\n      const next = new URLSearchParams(prev);\n      next.set('sort', field);   \u002F\u002F update one key\n      next.delete('page');       \u002F\u002F reset pagination on sort change\n      return next;\n    });\n  }\n\n  return \u003Cbutton onClick={() => handleSort('price')}>Sort by price\u003C\u002Fbutton>;\n}\n```\n\n**Rule of thumb:** Always use the **callback form** of `setSearchParams` when you need to preserve existing params — passing a plain object silently drops every key you don't include.\n",{"id":1944,"difficulty":121,"q":1945,"a":1946},"use-search-params-replace","How do you control whether setSearchParams pushes or replaces the history entry?","Pass `{ replace: true }` as the **second argument** to `setSearchParams` to replace the current history entry instead of pushing a new one. This is important for filters and sort controls where you don't want the Back button to undo each individual filter change.\n\n```jsx\nfunction FilterPanel() {\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  function setFilter(key, value) {\n    setSearchParams(\n      prev => { const n = new URLSearchParams(prev); n.set(key, value); return n; },\n      { replace: true } \u002F\u002F don't pollute the history stack with every filter tick\n    );\n  }\n\n  return \u003Cinput onChange={e => setFilter('q', e.target.value)} \u002F>;\n}\n```\n\n**Rule of thumb:** Use `replace: true` for search\u002Ffilter inputs that update on every keystroke; use the default push for intentional navigation actions the user might want to undo.\n",{"id":1948,"difficulty":121,"q":1949,"a":1950},"use-match-basic","What does useMatch do and what does it return?","**useMatch** tests whether the current URL matches a given **path pattern** and returns a **match object** with `params`, `pathname`, and `pathnameBase` if it matches, or `null` if it does not. It is useful for styling active links or conditionally rendering UI based on route context.\n\n```jsx\nimport { useMatch } from 'react-router-dom';\n\nfunction NavItem({ to, label }) {\n  \u002F\u002F Returns non-null only when the current URL matches '\u002Fsettings\u002F*'\n  const match = useMatch({ path: to, end: false });\n\n  return (\n    \u003Ca\n      href={to}\n      style={{ fontWeight: match ? 'bold' : 'normal' }} \u002F\u002F highlight active section\n    >\n      {label}\n    \u003C\u002Fa>\n  );\n}\n```\n\n**Rule of thumb:** Use `end: false` to match a prefix (like `\u002Fsettings` matching `\u002Fsettings\u002Fprofile`); use the default `end: true` for exact matches only.\n",{"id":1952,"difficulty":208,"q":1953,"a":1954},"use-routes-dynamic-config","What is useRoutes and when would you choose it over JSX route declarations?","**useRoutes** accepts a **route config array** (the same shape as the `\u003CRoutes>\u002F\u003CRoute>` JSX tree) and returns the matched element or `null`. It is the hook-based equivalent of `\u003CRoutes>` and is useful when route configuration is **data-driven** — loaded from an API, a CMS, or a permissions table.\n\n```jsx\nimport { useRoutes } from 'react-router-dom';\n\nconst routes = [\n  { path: '\u002F', element: \u003CHome \u002F> },\n  {\n    path: '\u002Fdashboard',\n    element: \u003CDashboardLayout \u002F>,\n    children: [\n      { index: true, element: \u003CDashboardHome \u002F> },\n      { path: 'reports', element: \u003CReports \u002F> },\n    ],\n  },\n  { path: '*', element: \u003CNotFound \u002F> },\n];\n\nfunction App() {\n  \u002F\u002F Renders whichever element matches the current URL\n  const element = useRoutes(routes);\n  return element;\n}\n```\n\n**Rule of thumb:** Use `useRoutes` when your route tree is dynamic or generated at runtime; prefer declarative JSX `\u003CRoutes>` for static trees because it is easier to read at a glance.\n",{"id":1956,"difficulty":208,"q":1957,"a":1958},"use-link-click-handler","What is useLinkClickHandler and when would you need it?","**useLinkClickHandler** returns an **onClick handler** for a custom anchor element that performs client-side navigation the same way `\u003CLink>` does — handling modifier keys (Ctrl\u002FCmd-click to open in new tab), `target` attributes, and `event.preventDefault()`. Use it when you need a fully custom link component that cannot extend `\u003CLink>`.\n\n```jsx\nimport { useLinkClickHandler } from 'react-router-dom';\n\n\u002F\u002F A styled anchor that wraps an icon + label but still SPA-navigates\nfunction FancyLink({ to, children, ...rest }) {\n  const handleClick = useLinkClickHandler(to, {\n    replace: false, \u002F\u002F default push behavior\n    state: { from: 'fancy' },\n  });\n\n  return (\n    \u002F\u002F Must be a real \u003Ca> so the browser handles right-click → \"Open in new tab\"\n    \u003Ca href={to} onClick={handleClick} {...rest}>\n      {children}\n    \u003C\u002Fa>\n  );\n}\n```\n\n**Rule of thumb:** Only reach for `useLinkClickHandler` when you cannot use `\u003CLink>` or `\u003CNavLink>` as the root element — for most cases those components are sufficient.\n",{"id":1960,"difficulty":121,"q":1961,"a":1962},"use-href","What does useHref return and how is it different from just using the path string directly?","**useHref** converts a **to** value (relative or absolute path) into a **fully resolved href string** that accounts for any `basename` configured on the router. If your app is mounted at `\u002Fapp`, `useHref('\u002Fdashboard')` returns `\u002Fapp\u002Fdashboard`, whereas a hardcoded string would break.\n\n```jsx\nimport { useHref } from 'react-router-dom';\n\nfunction ExternalShareButton({ to }) {\n  \u002F\u002F Resolves against the router's basename, giving a correct absolute path\n  const href = useHref(to);\n  const fullUrl = `${window.location.origin}${href}`;\n\n  return (\n    \u003Cbutton onClick={() => navigator.clipboard.writeText(fullUrl)}>\n      Copy link\n    \u003C\u002Fbutton>\n  );\n}\n```\n\n**Rule of thumb:** Use `useHref` whenever you need the resolved URL as a string (for clipboard, meta tags, or non-anchor elements) rather than navigating directly — it is the basename-aware alternative to manually concatenating strings.\n",{"id":1964,"difficulty":208,"q":1965,"a":1966},"use-blocker-pattern","How do you block navigation with useBlocker in React Router v6.4+?","**useBlocker** accepts a **condition function** that receives `{ currentLocation, nextLocation, historyAction }` and returns `true` to block. When blocked, it returns a **blocker object** with `state: 'blocked'` and `proceed()`\u002F`reset()` methods to confirm or cancel.\n\n```jsx\nimport { useBlocker } from 'react-router-dom';\n\nfunction EditForm({ isDirty }) {\n  const blocker = useBlocker(\n    \u002F\u002F Block only when there are unsaved changes\n    ({ currentLocation, nextLocation }) =>\n      isDirty && currentLocation.pathname !== nextLocation.pathname\n  );\n\n  return (\n    \u003C>\n      \u003Cform>...\u003C\u002Fform>\n      {blocker.state === 'blocked' && (\n        \u003Cdialog open>\n          \u003Cp>You have unsaved changes. Leave anyway?\u003C\u002Fp>\n          \u003Cbutton onClick={blocker.proceed}>Leave\u003C\u002Fbutton>   {\u002F* confirm *\u002F}\n          \u003Cbutton onClick={blocker.reset}>Stay\u003C\u002Fbutton>       {\u002F* cancel *\u002F}\n        \u003C\u002Fdialog>\n      )}\n    \u003C\u002F>\n  );\n}\n```\n\n**Rule of thumb:** Always call either `blocker.proceed()` or `blocker.reset()` to resolve a blocked navigation — leaving the blocker in `'blocked'` state permanently locks the app.\n",{"id":1968,"difficulty":121,"q":1969,"a":1970},"navigate-in-render-mistake","What is wrong with calling navigate() directly inside the render function of a component?","Calling `navigate()` **during render** triggers a navigation while React is still rendering, causing an immediate re-render loop and the React warning \"Cannot update a component while rendering a different component.\" Navigation must always happen in **event handlers** or inside a `useEffect`.\n\n```jsx\n\u002F\u002F ❌ Wrong — navigate called during render causes infinite re-render\nfunction RedirectIfLoggedOut({ user }) {\n  const navigate = useNavigate();\n  if (!user) navigate('\u002Flogin'); \u002F\u002F fires on every render pass\n  return \u003CDashboard \u002F>;\n}\n\n\u002F\u002F ✅ Correct — navigate called inside useEffect, runs after render\nfunction RedirectIfLoggedOut({ user }) {\n  const navigate = useNavigate();\n  useEffect(() => {\n    if (!user) navigate('\u002Flogin', { replace: true });\n  }, [user, navigate]);\n  return user ? \u003CDashboard \u002F> : null;\n}\n```\n\n**Rule of thumb:** If you find yourself writing `navigate()` outside of an event handler or `useEffect`, stop — you are almost certainly in a render path and will cause an infinite loop.\n",{"id":1972,"difficulty":121,"q":1973,"a":1974},"navigate-vs-history-back","What is the difference between navigate(-1) and window.history.back()?","Both navigate back one history entry, but `navigate(-1)` goes through **React Router's transition system** while `window.history.back()` is a raw browser API call. This means React Router's **blockers**, **scroll restoration**, and **transition listeners** fire for `navigate(-1)` but are bypassed by `window.history.back()`.\n\n```jsx\nfunction BackButton() {\n  const navigate = useNavigate();\n\n  return (\n    \u003C>\n      {\u002F* ✅ Preferred — triggers React Router's blockers and listeners *\u002F}\n      \u003Cbutton onClick={() => navigate(-1)}>Back (React Router)\u003C\u002Fbutton>\n\n      {\u002F* ❌ Avoid — skips useBlocker, no transition event, hard to test *\u002F}\n      \u003Cbutton onClick={() => window.history.back()}>Back (native)\u003C\u002Fbutton>\n    \u003C\u002F>\n  );\n}\n```\n\n**Rule of thumb:** Always use `navigate(-1)` in React Router apps so any registered blockers (unsaved-form guards) and navigation listeners fire correctly.\n",{"id":1976,"difficulty":121,"q":1977,"a":1978},"location-state-persist","How does location.state persist across navigations and what are its limitations?","**location.state** is stored in the **browser's History API entry** for that specific navigation. It persists as long as the user stays in the session history — navigating back then forward re-exposes the same state. However, it is **lost on a hard refresh** (F5) and is **not visible in the URL**, so it cannot be bookmarked or shared.\n\n```jsx\n\u002F\u002F Send state when navigating to a list item\nnavigate(`\u002Forders\u002F${id}`, { state: { returnPath: '\u002Forders', filters } });\n\n\u002F\u002F Read state on the destination page\nfunction OrderDetail() {\n  const { state } = useLocation();\n  const returnPath = state?.returnPath ?? '\u002F'; \u002F\u002F graceful fallback for direct visits\n\n  return (\n    \u003Cbutton onClick={() => navigate(returnPath, { state: state?.filters })}>\n      ← Back to orders\n    \u003C\u002Fbutton>\n  );\n}\n```\n\n**Rule of thumb:** Use `location.state` for ephemeral UI data (breadcrumb hints, scroll position, pre-filled values); always provide a sensible fallback for when the user arrives directly via a bookmarked URL.\n",{"id":1980,"difficulty":208,"q":1981,"a":1982},"use-params-optional-segment","How do you handle optional URL parameters when using useParams?","React Router v6 does not support the `?` optional segment syntax directly. The idiomatic approach is to declare **two sibling routes** — one with the segment and one without — and let each render the same component. Inside the component, `useParams` returns `undefined` for the missing key when the shorter route matches.\n\n```jsx\n\u002F\u002F Route config — two routes, one component\n\u003CRoutes>\n  \u003CRoute path=\"\u002Freports\u002F:year\u002F:month\" element={\u003CReportPage \u002F>} \u002F>\n  \u003CRoute path=\"\u002Freports\u002F:year\"        element={\u003CReportPage \u002F>} \u002F>\n\u003C\u002FRoutes>\n\nfunction ReportPage() {\n  const { year, month } = useParams();\n  \u002F\u002F month is undefined when only \u002Freports\u002F2026 is visited\n  const label = month ? `${year}\u002F${month}` : `${year} (all months)`;\n\n  return \u003Ch1>{label}\u003C\u002Fh1>;\n}\n```\n\n**Rule of thumb:** Avoid trying to make a single route handle both cases with regex tricks — two explicit routes are clearer, more maintainable, and easier to test.\n",{"id":1984,"difficulty":121,"q":1985,"a":1986},"use-search-params-vs-state","When should you use useSearchParams instead of useState for managing filter values?","Use **useSearchParams** when the filter state should be **shareable, bookmarkable, or survive a refresh** — the URL is the source of truth. Use `useState` for purely ephemeral UI state (dropdown open\u002Fclose, hover effects) that has no meaning outside the current render.\n\n```jsx\n\u002F\u002F ✅ useSearchParams — user can share\u002Fbookmark filtered results\nfunction ProductFilter() {\n  const [searchParams, setSearchParams] = useSearchParams();\n  const category = searchParams.get('category') ?? 'all';\n\n  return (\n    \u003Cselect\n      value={category}\n      onChange={e =>\n        setSearchParams({ category: e.target.value }, { replace: true })\n      }\n    >\n      \u003Coption value=\"all\">All\u003C\u002Foption>\n      \u003Coption value=\"books\">Books\u003C\u002Foption>\n    \u003C\u002Fselect>\n  );\n}\n```\n\n**Rule of thumb:** If a user refreshing the page or copying the URL should see the same results, that state belongs in the URL (`useSearchParams`); if it is purely visual\u002Ftransient, `useState` is sufficient.\n",{"id":1988,"difficulty":121,"q":1989,"a":1990},"use-match-end-option","What does the end option in useMatch control and when should you set it to false?","The **end** option (default `true`) controls whether the pattern must match the **entire pathname** or just a **prefix**. With `end: true`, `\u002Fsettings` only matches the exact path `\u002Fsettings`. With `end: false`, it matches `\u002Fsettings`, `\u002Fsettings\u002Fprofile`, `\u002Fsettings\u002Fbilling`, and so on — useful for highlighting a parent nav item when any child route is active.\n\n```jsx\nfunction SettingsNav() {\n  \u002F\u002F end: false — active whenever any \u002Fsettings\u002F* route is rendered\n  const settingsMatch = useMatch({ path: '\u002Fsettings', end: false });\n  \u002F\u002F end: true (default) — active only on the exact \u002Fsettings page\n  const exactMatch  = useMatch('\u002Fsettings');\n\n  return (\n    \u003Cnav>\n      \u003Ca style={{ fontWeight: settingsMatch ? 'bold' : 'normal' }}\n         href=\"\u002Fsettings\">\n        Settings {settingsMatch && '(active section)'}\n      \u003C\u002Fa>\n    \u003C\u002Fnav>\n  );\n}\n```\n\n**Rule of thumb:** Set `end: false` for top-level nav items that have child routes; use the default `end: true` (or just pass the path string) for leaf routes where exact matching matters.\n",{"description":94},"React Router v6 navigation hooks interview questions — useNavigate, useParams, useLocation, useSearchParams, useMatch, useBlocker, and programmatic navigation patterns.","react\u002Frouting\u002Fnavigation-hooks","abQ4KFyCLOvgdO16RQn_HXrPwd96ieDJPJmP7kOkrlQ",{"id":1996,"title":1997,"body":1998,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":2002,"navigation":100,"order":37,"path":2003,"questions":2004,"questionsCount":1237,"related":169,"seo":2061,"seoDescription":2062,"stem":2063,"subtopic":2064,"topic":36,"topicSlug":38,"updated":420,"__hash__":2065},"qa\u002Freact\u002Fstate-and-data-flow\u002Fcontrolled-vs-uncontrolled.md","Controlled Vs Uncontrolled",{"type":91,"value":1999,"toc":2000},[],{"title":94,"searchDepth":11,"depth":11,"links":2001},[],{"subtopicSlug":248},"\u002Freact\u002Fstate-and-data-flow\u002Fcontrolled-vs-uncontrolled",[2005,2009,2013,2017,2021,2025,2029,2033,2037,2041,2045,2049,2053,2057],{"id":2006,"difficulty":96,"q":2007,"a":2008},"controlled-what","What is a controlled component in React?","A **controlled component** is one where React state is the **single\nsource of truth** for the input's value. The displayed value comes from\nstate; every change must go through state via an `onChange` handler.\n\n```jsx\nfunction ControlledInput() {\n  const [value, setValue] = useState('')\n\n  return (\n    \u003Cinput\n      value={value}                         \u002F\u002F state drives the DOM\n      onChange={e => setValue(e.target.value)} \u002F\u002F DOM change updates state\n    \u002F>\n  )\n}\n```\n\nReact keeps the DOM perfectly in sync with state — you can read,\nvalidate, or transform every keystroke before it appears.\n\n**Rule of thumb:** If you need to validate, transform, or react to every\nchange as the user types, use a controlled component.\n",{"id":2010,"difficulty":96,"q":2011,"a":2012},"uncontrolled-what","What is an uncontrolled component in React?","An **uncontrolled component** lets the DOM manage its own state. React\ndoes not drive the input's value — you only read it when you need it\n(typically on submit) using a `ref`.\n\n```jsx\nfunction UncontrolledForm() {\n  const inputRef = useRef(null)\n\n  function handleSubmit(e) {\n    e.preventDefault()\n    console.log(inputRef.current.value)   \u002F\u002F read on demand\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput ref={inputRef} defaultValue=\"initial\" \u002F>\n      \u003Cbutton type=\"submit\">Submit\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\nBecause there is no React state involved, uncontrolled inputs are\nsimpler to set up but offer less control over intermediate values.\n\n**Rule of thumb:** Use uncontrolled components for simple forms where\nyou only care about the final submitted value, not intermediate input.\n",{"id":2014,"difficulty":96,"q":2015,"a":2016},"controlled-vs-uncontrolled-key-diff","What is the key difference between the `value` and `defaultValue` props on an input?","| Prop | Type | Behaviour |\n|---|---|---|\n| `value` | Controlled | React always overrides the DOM value; component is controlled |\n| `defaultValue` | Uncontrolled | Sets the initial value once; DOM manages value after that |\n\n```jsx\n\u002F\u002F Controlled — React owns the value at all times\n\u003Cinput value={stateValue} onChange={e => setState(e.target.value)} \u002F>\n\n\u002F\u002F Uncontrolled — React only sets the initial value\n\u003Cinput defaultValue=\"hello\" ref={inputRef} \u002F>\n```\n\nMixing `value` without `onChange` makes the input read-only from the\nuser's perspective (React prevents edits). React will warn you.\n\n**Rule of thumb:** Use `value` when React should own the value;\nuse `defaultValue` when the DOM should own it after mount.\n",{"id":2018,"difficulty":96,"q":2019,"a":2020},"controlled-when-to-use","When should you prefer a controlled component over an uncontrolled one?","Prefer controlled components when you need:\n- **Instant validation** (highlight errors as the user types)\n- **Conditional UI** based on current field value\n- **Dynamic constraints** (disable a button until all fields are valid)\n- **Programmatic value changes** (auto-fill, clear on reset)\n\n```jsx\nfunction EmailField() {\n  const [email, setEmail] = useState('')\n  const isValid = email.includes('@')\n\n  return (\n    \u003C>\n      \u003Cinput\n        value={email}\n        onChange={e => setEmail(e.target.value)}\n        className={isValid ? 'valid' : 'invalid'}\n      \u002F>\n      \u003Cbutton disabled={!isValid}>Subscribe\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\n**Rule of thumb:** Most production forms use controlled inputs because\nthey provide the feedback loop needed for good UX.\n",{"id":2022,"difficulty":96,"q":2023,"a":2024},"uncontrolled-when-to-use","When is an uncontrolled component a better choice?","Uncontrolled inputs are simpler when:\n- You only need the value at submit time (e.g. a basic search box)\n- Integrating with **non-React libraries** that manage their own DOM\n- Handling **file inputs** (`\u003Cinput type=\"file\">` is always uncontrolled\n  because the browser controls the file path for security)\n- Rapid prototyping where wiring up state would slow you down\n\n```jsx\n\u002F\u002F File inputs are always uncontrolled\nfunction FileUpload() {\n  const fileRef = useRef(null)\n\n  function handleUpload() {\n    const file = fileRef.current.files[0]\n    upload(file)\n  }\n\n  return (\n    \u003C>\n      \u003Cinput type=\"file\" ref={fileRef} \u002F>\n      \u003Cbutton onClick={handleUpload}>Upload\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\n**Rule of thumb:** `\u003Cinput type=\"file\">` is always uncontrolled; for\neverything else, default to controlled unless simplicity wins.\n",{"id":2026,"difficulty":96,"q":2027,"a":2028},"controlled-warning-read-only","What warning does React show if you set `value` without an `onChange` handler?","React logs:\n> *\"You provided a `value` prop to a form field without an `onChange`\n> handler. This will render a read-only field.\"*\n\nThe input is effectively frozen — the user types but the DOM value is\nalways reset to `value` by React on every render.\n\n```jsx\n\u002F\u002F ❌ Read-only — React overrides every keystroke\n\u003Cinput value=\"fixed\" \u002F>\n\n\u002F\u002F ✅ Controlled with handler\n\u003Cinput value={state} onChange={e => setState(e.target.value)} \u002F>\n\n\u002F\u002F ✅ Or explicitly read-only with readOnly\n\u003Cinput value=\"fixed\" readOnly \u002F>\n```\n\n**Rule of thumb:** If you pass `value`, you must also pass `onChange`\n(or `readOnly` if intentionally read-only).\n",{"id":2030,"difficulty":121,"q":2031,"a":2032},"controlled-null-value","What happens if you pass `null` or `undefined` as the `value` prop?","Passing `null` or `undefined` makes the input **uncontrolled** — React\ntreats it the same as omitting `value`. If you later switch back to a\nreal value, React warns that you're changing a component from\nuncontrolled to controlled.\n\n```jsx\n\u002F\u002F ❌ Starts uncontrolled (value is null initially)\nconst [text, setText] = useState(null)\n\u003Cinput value={text} onChange={e => setText(e.target.value)} \u002F>\n\u002F\u002F React: \"changing an uncontrolled input to be controlled\"\n\n\u002F\u002F ✅ Always start controlled with an empty string\nconst [text, setText] = useState('')\n\u003Cinput value={text} onChange={e => setText(e.target.value)} \u002F>\n```\n\n**Rule of thumb:** Initialise controlled state with an empty string\n(`''`) rather than `null` or `undefined`.\n",{"id":2034,"difficulty":96,"q":2035,"a":2036},"controlled-textarea-select","How do controlled and uncontrolled patterns apply to textarea and select elements?","The same `value`\u002F`defaultValue` pattern applies to `\u003Ctextarea>` and\n`\u003Cselect>`. React normalises them so they behave consistently.\n\n```jsx\n\u002F\u002F Controlled textarea\n\u003Ctextarea value={text} onChange={e => setText(e.target.value)} \u002F>\n\n\u002F\u002F Controlled select\n\u003Cselect value={selected} onChange={e => setSelected(e.target.value)}>\n  \u003Coption value=\"a\">Option A\u003C\u002Foption>\n  \u003Coption value=\"b\">Option B\u003C\u002Foption>\n\u003C\u002Fselect>\n\n\u002F\u002F Uncontrolled select with initial selection\n\u003Cselect defaultValue=\"b\" ref={selectRef}>\n  \u003Coption value=\"a\">Option A\u003C\u002Foption>\n  \u003Coption value=\"b\">Option B\u003C\u002Foption>\n\u003C\u002Fselect>\n```\n\n**Rule of thumb:** Treat `\u003Ctextarea>` and `\u003Cselect>` exactly like\n`\u003Cinput>` — use `value` for controlled, `defaultValue` for uncontrolled.\n",{"id":2038,"difficulty":96,"q":2039,"a":2040},"controlled-ref-reading","Can you read an uncontrolled input's value without a ref?","Technically yes — via the native form submit event's\n`e.target.elements` map — but `ref` is the idiomatic React approach.\n\n```jsx\n\u002F\u002F Using ref (recommended)\nconst inputRef = useRef(null)\n\u003Cinput ref={inputRef} \u002F>\n\u002F\u002F Read: inputRef.current.value\n\n\u002F\u002F Using the form submit event (also works, no ref needed)\nfunction handleSubmit(e) {\n  e.preventDefault()\n  const value = e.target.elements.username.value\n}\n\u003Cform onSubmit={handleSubmit}>\n  \u003Cinput name=\"username\" \u002F>\n\u003C\u002Fform>\n```\n\nThe `name`-based approach is fine for very simple forms and mirrors\ntraditional HTML form handling.\n\n**Rule of thumb:** Use `ref` for programmatic access during the\ncomponent's lifetime; use `e.target.elements` for one-shot submit\nreading.\n",{"id":2042,"difficulty":121,"q":2043,"a":2044},"controlled-mixed-form","Can a single form have both controlled and uncontrolled inputs?","Technically yes, but it's an anti-pattern. Mixing paradigms makes the\ncode harder to reason about and breaks the \"single source of truth\"\nprinciple.\n\n```jsx\n\u002F\u002F ❌ Confusing mix\nfunction Form() {\n  const [email, setEmail] = useState('')   \u002F\u002F controlled\n  const phoneRef = useRef(null)            \u002F\u002F uncontrolled\n\n  function handleSubmit() {\n    const data = { email, phone: phoneRef.current.value }\n    \u002F\u002F collecting values from two different sources\n  }\n}\n\n\u002F\u002F ✅ Consistent — all controlled or all via refs\n```\n\nIf you choose controlled, keep all fields controlled. If you use a\nlibrary like React Hook Form it manages uncontrolled inputs uniformly.\n\n**Rule of thumb:** Pick one pattern per form and stick to it.\n",{"id":2046,"difficulty":121,"q":2047,"a":2048},"controlled-react-hook-form","How does React Hook Form differ from the traditional controlled component pattern?","React Hook Form uses **uncontrolled inputs by default** (via `ref`),\nsubscribing to change events without storing every keystroke in React\nstate. This results in **fewer re-renders** — the component only\nre-renders when validation state changes or on submit.\n\n```jsx\nimport { useForm } from 'react-hook-form'\n\nfunction SignupForm() {\n  const { register, handleSubmit, formState: { errors } } = useForm()\n\n  return (\n    \u003Cform onSubmit={handleSubmit(data => console.log(data))}>\n      {\u002F* register wires up ref + onChange internally *\u002F}\n      \u003Cinput {...register('email', { required: true })} \u002F>\n      {errors.email && \u003Cspan>Email required\u003C\u002Fspan>}\n      \u003Cbutton type=\"submit\">Sign up\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\nFor controlled integration use the `Controller` component or\n`useController` hook.\n\n**Rule of thumb:** React Hook Form is the pragmatic choice for\ncomplex forms — it gives controlled-like DX with uncontrolled\nperformance.\n",{"id":2050,"difficulty":121,"q":2051,"a":2052},"controlled-reset","How do you programmatically reset a controlled form to its initial values?","Call the state setter with the original default values. Because the\ninput values are driven by React state, setting state is all you need.\n\n```jsx\nconst INITIAL = { name: '', email: '' }\n\nfunction ProfileForm() {\n  const [form, setForm] = useState(INITIAL)\n\n  function reset() {\n    setForm(INITIAL)    \u002F\u002F inputs snap back to empty\n  }\n\n  return (\n    \u003Cform>\n      \u003Cinput value={form.name}  onChange={e => setForm(p => ({ ...p, name: e.target.value }))} \u002F>\n      \u003Cinput value={form.email} onChange={e => setForm(p => ({ ...p, email: e.target.value }))} \u002F>\n      \u003Cbutton type=\"button\" onClick={reset}>Reset\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\nFor uncontrolled forms you need to call `inputRef.current.value = ''`\nfor each field manually, which is why controlled inputs are easier to\nreset programmatically.\n\n**Rule of thumb:** Controlled components make reset trivial — just\nrestore state. Uncontrolled forms require manual DOM manipulation.\n",{"id":2054,"difficulty":208,"q":2055,"a":2056},"controlled-dynamic-fields","How do you manage a dynamic list of controlled input fields?","Store the list in state as an array. Each field reads from its array\nslot and writes back using its index or a stable id.\n\n```jsx\nfunction TagsInput() {\n  const [tags, setTags] = useState(['react', 'hooks'])\n\n  function handleChange(index, value) {\n    setTags(prev => prev.map((t, i) => i === index ? value : t))\n  }\n\n  function addTag() {\n    setTags(prev => [...prev, ''])\n  }\n\n  function removeTag(index) {\n    setTags(prev => prev.filter((_, i) => i !== index))\n  }\n\n  return (\n    \u003C>\n      {tags.map((tag, i) => (\n        \u003Cdiv key={i}>\n          \u003Cinput value={tag} onChange={e => handleChange(i, e.target.value)} \u002F>\n          \u003Cbutton onClick={() => removeTag(i)}>✕\u003C\u002Fbutton>\n        \u003C\u002Fdiv>\n      ))}\n      \u003Cbutton onClick={addTag}>+ Add\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\n**Rule of thumb:** Use stable ids rather than array indices as `key`\nwhen items can be reordered to avoid React losing focus state.\n",{"id":2058,"difficulty":96,"q":2059,"a":2060},"controlled-vs-uncontrolled-interview-summary","How would you summarise the controlled vs. uncontrolled tradeoff in one sentence?","**Controlled** components give React full authority over the input value\nat the cost of wiring up `onChange` state; **uncontrolled** components\nlet the DOM own the value and are simpler to set up but harder to\nintegrate with validation and dynamic UI.\n\n```\nControlled  → React state is truth → more wiring, more power\nUncontrolled → DOM is truth         → less wiring, less control\n```\n\nThe React docs historically recommended controlled components for most\ncases; today, form libraries (React Hook Form) offer a third path that\nis uncontrolled under the hood but feels controlled in the API.\n\n**Rule of thumb:** Default to controlled unless performance or\nthird-party DOM libraries push you toward uncontrolled.\n",{"description":94},"React controlled vs uncontrolled components interview questions — value vs defaultValue, refs, form handling, when to use each approach.","react\u002Fstate-and-data-flow\u002Fcontrolled-vs-uncontrolled","Controlled vs Uncontrolled Components","pcInXbHq2wIIioyp57ydXhnhjx7QfGClTxyTTNAfRrI",{"id":2067,"title":2068,"body":2069,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":2073,"navigation":100,"order":37,"path":2074,"questions":2075,"questionsCount":1495,"related":169,"seo":2147,"seoDescription":2148,"stem":2149,"subtopic":2150,"topic":63,"topicSlug":65,"updated":420,"__hash__":2151},"qa\u002Freact\u002Fstate-management\u002Fcontext-vs-redux.md","Context Vs Redux",{"type":91,"value":2070,"toc":2071},[],{"title":94,"searchDepth":11,"depth":11,"links":2072},[],{},"\u002Freact\u002Fstate-management\u002Fcontext-vs-redux",[2076,2080,2084,2088,2092,2096,2100,2104,2108,2111,2115,2119,2123,2127,2131,2135,2139,2143],{"id":2077,"difficulty":96,"q":2078,"a":2079},"context-api-what-is-it","What is the Context API and what is its primary use case?","The **Context API** is React's built-in mechanism for sharing values across\na component tree without passing props through every intermediate layer. You\ncreate a context with `createContext`, wrap a subtree in its `Provider`, and\nany descendant can read the value with `useContext`.\n\n```jsx\nconst ThemeContext = createContext('light')  \u002F\u002F default value\n\nfunction App() {\n  return (\n    \u003CThemeContext.Provider value=\"dark\">\n      \u003CLayout \u002F>                 {\u002F* no prop threading needed *\u002F}\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\nfunction Button() {\n  const theme = useContext(ThemeContext)  \u002F\u002F reads 'dark' directly\n  return \u003Cbutton className={theme}>Click\u003C\u002Fbutton>\n}\n```\n\nIts primary use case is **ambient, infrequently-updated global data** —\nthe authenticated user, current locale, colour theme, or feature flags.\nThese values are read often but change rarely, so the re-render cost is\nlow.\n\n**Rule of thumb:** Context is a dependency-injection mechanism, not a\nstate manager. Reach for it when you want to avoid prop drilling, not when\nyou need a sophisticated state architecture.\n",{"id":2081,"difficulty":96,"q":2082,"a":2083},"context-vs-redux-mental-model","What is the core mental model difference between Context and Redux?","**Context** is a *transport layer* — it teleports a value from a Provider\nto any consumer without passing props. It owns no opinion about how state\nis structured or updated; you still call `useState`\u002F`useReducer` yourself.\n\n**Redux** is a *state architecture* — a single, centralised store with\nstrict rules: state is read-only, updates happen through serialisable\n**actions**, and **reducers** are pure functions that produce the next\nstate. Middleware, selectors, and DevTools are first-class concepts.\n\n```js\n\u002F\u002F Context — you manage the state, Context just shares it\nconst [user, setUser] = useState(null)\n\u003CUserContext.Provider value={{ user, setUser }}>...\u003C\u002FUserContext.Provider>\n\n\u002F\u002F Redux — the store manages everything; components just dispatch\u002Fselect\ndispatch({ type: 'auth\u002Flogin', payload: { id: 1, name: 'Ada' } })\nconst user = useSelector(state => state.auth.user)\n```\n\nThink of it this way: Context answers \"how do I share state?\" — Redux\nanswers \"how do I manage state?\"\n\n**Rule of thumb:** If you find yourself bolting a reducer, middleware, and\nselector logic onto Context, you've reinvented Redux — just use Redux.\n",{"id":2085,"difficulty":121,"q":2086,"a":2087},"context-re-render-problem","Why does Context cause excessive re-renders and when does it become a problem?","Every component that calls `useContext(MyContext)` re-renders **whenever\nthe Provider's `value` reference changes** — regardless of whether the\nspecific slice of that value the component uses actually changed. React\nhas no built-in selector granularity for Context.\n\n```jsx\nconst AppContext = createContext()\n\nfunction Provider({ children }) {\n  const [user, setUser] = useState(null)\n  const [cart, setCart] = useState([])\n\n  \u002F\u002F New object every render — both user AND cart consumers re-render\n  \u002F\u002F even if only `cart` changed\n  return (\n    \u003CAppContext.Provider value={{ user, setUser, cart, setCart }}>\n      {children}\n    \u003C\u002FAppContext.Provider>\n  )\n}\n\nfunction UserBadge() {\n  const { user } = useContext(AppContext)  \u002F\u002F re-renders on every cart update too\n  return \u003Cspan>{user?.name}\u003C\u002Fspan>\n}\n```\n\nThis becomes a real problem when the context value updates **frequently**\n(e.g. every keystroke, every animation frame, a live-updating cart) and\nmany components are subscribed.\n\n**Rule of thumb:** Context re-render overhead is acceptable for values\nthat change a few times per session; it becomes a bottleneck for values\nthat change multiple times per second or that are consumed by dozens of\ncomponents.\n",{"id":2089,"difficulty":96,"q":2090,"a":2091},"context-good-fit","When is Context sufficient — what kinds of state does it handle well?","Context shines for **infrequently-updated, app-wide ambient data** where\nyou need broad access but not granular subscriptions:\n\n- **Auth user** — set on login\u002Flogout, read in many places, almost never\n  changes during a session.\n- **Theme \u002F colour mode** — toggled once in a while, consumed by many\n  components for styling.\n- **Locale \u002F i18n** — changes only when the user switches language.\n- **Feature flags** — loaded once at startup.\n- **UI preferences** — sidebar collapsed, modal stack.\n\n```jsx\n\u002F\u002F A practical, well-scoped auth context\nconst AuthContext = createContext(null)\n\nexport function AuthProvider({ children }) {\n  const [user, setUser] = useState(null)\n  const login  = useCallback(async (creds) => { \u002F* ... *\u002F }, [])\n  const logout = useCallback(() => setUser(null), [])\n\n  \u002F\u002F Memoised so reference only changes when user changes\n  const value = useMemo(() => ({ user, login, logout }), [user, login, logout])\n  return \u003CAuthContext.Provider value={value}>{children}\u003C\u002FAuthContext.Provider>\n}\n```\n\n**Rule of thumb:** If the value changes less than once per user action,\nContext is probably the right tool.\n",{"id":2093,"difficulty":121,"q":2094,"a":2095},"redux-when-necessary","When does an app outgrow Context and actually need Redux?","Redux earns its keep when several of these conditions are true:\n\n1. **High-frequency updates** — shopping carts, real-time feeds, form\n   state with many interdependent fields.\n2. **Complex async** — sequences of API calls with loading\u002Ferror\u002Fretry\n   states that need to be cancelled, debounced, or coordinated.\n3. **Cross-cutting derived state** — selectors that combine slices from\n   multiple parts of the store.\n4. **Large team** — enforced conventions (action types, reducer\n   boundaries) prevent accidental coupling.\n5. **Audit \u002F replay requirements** — you need a full log of every state\n   change for debugging or analytics.\n\n```js\n\u002F\u002F Redux Toolkit slice — clear action\u002Freducer contract\nconst cartSlice = createSlice({\n  name: 'cart',\n  initialState: { items: [], status: 'idle' },\n  reducers: {\n    addItem:    (state, action) => { state.items.push(action.payload) },\n    removeItem: (state, action) => { state.items = state.items.filter(i => i.id !== action.payload) },\n  },\n})\n```\n\n**Rule of thumb:** If you start adding \"Redux-like\" patterns to Context\n(reducers, action types, middleware), stop and use Redux Toolkit instead.\n",{"id":2097,"difficulty":121,"q":2098,"a":2099},"splitting-contexts","How do you split contexts to reduce unnecessary re-renders?","The key insight is that each `useContext` call subscribes to **one**\ncontext. If you split your monolithic context into smaller, focused\ncontexts, a component that consumes only one will not re-render when\nanother changes.\n\n```jsx\n\u002F\u002F ❌ One big context — UserBadge re-renders on every cart change\n\u003CAppContext.Provider value={{ user, cart }}>\n\n\u002F\u002F ✅ Separate contexts — UserBadge only subscribes to UserContext\nconst UserContext = createContext(null)\nconst CartContext = createContext(null)\n\nfunction Providers({ children }) {\n  const [user] = useState(null)\n  const [cart, setCart] = useState([])\n  return (\n    \u003CUserContext.Provider value={user}>\n      \u003CCartContext.Provider value={{ cart, setCart }}>\n        {children}\n      \u003C\u002FCartContext.Provider>\n    \u003C\u002FUserContext.Provider>\n  )\n}\n\nfunction UserBadge() {\n  const user = useContext(UserContext)  \u002F\u002F never re-renders for cart updates\n  return \u003Cspan>{user?.name}\u003C\u002Fspan>\n}\n```\n\nA good heuristic: each context should have a single reason to change.\nIf you find yourself subscribing to the same context from unrelated\ncomponents, split it.\n\n**Rule of thumb:** One concern per context; keep Providers lean so their\nvalue references are stable.\n",{"id":2101,"difficulty":121,"q":2102,"a":2103},"context-usereducer-pattern","How does Context + useReducer work as a lightweight Redux alternative?","Combining `useReducer` with Context gives you Redux's core pattern —\na single dispatch function, a reducer, and a read-only state view — with\nno external dependency.\n\n```jsx\nconst CounterStateContext   = createContext(null)\nconst CounterDispatchContext = createContext(null)  \u002F\u002F separate so consumers that only dispatch don't re-render on state changes\n\nfunction counterReducer(state, action) {\n  switch (action.type) {\n    case 'increment': return { count: state.count + 1 }\n    case 'decrement': return { count: state.count - 1 }\n    default: throw new Error(`Unknown action: ${action.type}`)\n  }\n}\n\nexport function CounterProvider({ children }) {\n  const [state, dispatch] = useReducer(counterReducer, { count: 0 })\n  return (\n    \u003CCounterStateContext.Provider value={state}>\n      \u003CCounterDispatchContext.Provider value={dispatch}>\n        {children}\n      \u003C\u002FCounterDispatchContext.Provider>\n    \u003C\u002FCounterStateContext.Provider>\n  )\n}\n\n\u002F\u002F Consuming components\nconst { count } = useContext(CounterStateContext)   \u002F\u002F reads state\nconst dispatch  = useContext(CounterDispatchContext) \u002F\u002F stable reference, never re-renders for state changes\n```\n\nThis pattern works well for small-to-medium apps. What it still lacks:\nmiddleware, DevTools, and selector memoisation.\n\n**Rule of thumb:** Context + useReducer is a great choice up to ~5 reducers;\nbeyond that the manual wiring overhead starts to rival Redux Toolkit's setup cost.\n",{"id":2105,"difficulty":208,"q":2106,"a":2107},"redux-middleware","What is Redux middleware and why can't Context replicate it natively?","**Middleware** sits between `dispatch` and the reducer, intercepts every\naction, and can transform, delay, cancel, or trigger side effects before\npassing the action along. The two dominant middleware are `redux-thunk`\n(async action creators) and `redux-saga` (complex async flows with\ngenerators).\n\n```js\n\u002F\u002F redux-thunk: dispatch a function instead of a plain object\nexport const fetchUser = (id) => async (dispatch, getState) => {\n  dispatch({ type: 'users\u002FfetchStart' })\n  try {\n    const user = await api.getUser(id)\n    dispatch({ type: 'users\u002FfetchSuccess', payload: user })\n  } catch (err) {\n    dispatch({ type: 'users\u002FfetchFailure', payload: err.message })\n  }\n}\n\n\u002F\u002F RTK wraps this with createAsyncThunk for zero boilerplate\nexport const fetchUser = createAsyncThunk('users\u002Ffetch', (id) => api.getUser(id))\n```\n\nContext has no equivalent. You can put an async function in a Context\nvalue, but you lose the action-log, DevTools integration, cancellation\ntokens, and the ability to chain\u002Fcancel at the middleware layer. Replicating\nsaga-level coordination (race conditions, debounce, takeLatest) in Context\nrequires re-implementing what middleware already provides.\n\n**Rule of thumb:** The moment you need async sequencing beyond a single\n`async\u002Fawait` call, Redux middleware (or React Query for data fetching)\npays for itself immediately.\n",{"id":725,"difficulty":96,"q":2109,"a":2110},"What does Redux DevTools offer and why is it valuable?","**Redux DevTools** is a browser extension (and built-in RTK feature) that\ngives you a full observable record of your application's state history.\nKey features:\n\n- **Action log** — every dispatched action listed in order.\n- **State diff** — exactly what changed in the store for each action.\n- **Time-travel debugging** — jump to any past state by clicking an entry\n  in the log; the UI re-renders as if it's at that point in time.\n- **Action replay** — replay a sequence of actions to reproduce a bug.\n- **Import\u002Fexport state** — share a state snapshot with a team member.\n\n```js\n\u002F\u002F RTK configures DevTools automatically in development\nimport { configureStore } from '@reduxjs\u002Ftoolkit'\n\nconst store = configureStore({\n  reducer: rootReducer,\n  \u002F\u002F devTools: true by default in dev, false in production\n})\n```\n\nContext has no equivalent. A console.log in a reducer or a React DevTools\ncomponent inspection is the closest you can get, but neither gives you\ntime-travel or serialisable action history.\n\n**Rule of thumb:** If you've ever said \"I wish I could rewind to see what\ncaused this bug,\" that's the DevTools use case — and it's only available\nwith Redux.\n",{"id":2112,"difficulty":121,"q":2113,"a":2114},"boilerplate-context-vs-rtk","How does the boilerplate of Context compare to Redux Toolkit?","Modern **Redux Toolkit (RTK)** has dramatically closed the boilerplate gap.\nCreating a slice, wiring a store, and connecting a component now requires\nfewer files than a well-structured Context setup.\n\n```js\n\u002F\u002F RTK: one createSlice call replaces action types + action creators + reducer\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment: state => { state.value += 1 },  \u002F\u002F Immer under the hood — mutate safely\n    decrement: state => { state.value -= 1 },\n  },\n})\n\nexport const { increment, decrement } = counterSlice.actions\nexport default counterSlice.reducer\n```\n\n```jsx\n\u002F\u002F Context equivalent needs: createContext, Provider component,\n\u002F\u002F useState or useReducer, custom hooks, and manual memoisation —\n\u002F\u002F often spread across 3–4 files for a non-trivial piece of state.\n```\n\nThe honest comparison in 2024: RTK setup is ~20 lines; a well-structured\nContext + useReducer setup for the same feature is similar or larger.\nThe Redux tax is now mostly in learning the mental model, not in line count.\n\n**Rule of thumb:** Don't avoid Redux for boilerplate reasons; the RTK API\nis concise. Avoid it because your state needs are genuinely simple.\n",{"id":2116,"difficulty":121,"q":2117,"a":2118},"usememo-context-value","How does useMemo help with Context performance and when is it required?","Every time a Provider's parent re-renders, its `value` prop is a new\nobject literal — which triggers re-renders in all consumers even if the\ndata is identical. **`useMemo`** stabilises the reference so consumers\nonly re-render when the underlying data actually changes.\n\n```jsx\nfunction UserProvider({ children }) {\n  const [user, setUser]   = useState(null)\n  const [token, setToken] = useState(null)\n\n  \u002F\u002F ❌ New object every render, even if user\u002Ftoken haven't changed\n  \u002F\u002F return \u003CCtx.Provider value={{ user, setUser, token, setToken }}>\n\n  \u002F\u002F ✅ Stable reference — consumers re-render only when user or token changes\n  const value = useMemo(\n    () => ({ user, setUser, token, setToken }),\n    [user, token]  \u002F\u002F setUser\u002FsetToken are stable (from useState)\n  )\n\n  return \u003CUserContext.Provider value={value}>{children}\u003C\u002FUserContext.Provider>\n}\n```\n\nWhen is it required? Whenever the Provider's parent can re-render for\nreasons unrelated to the context value — e.g., it lives at the top of the\ntree where any ancestor state change would cascade down.\n\n**Rule of thumb:** Always wrap a Context value object in `useMemo` if\nthe Provider is not at the very root of the tree; skip it only when\nthe Provider is a static singleton that never re-renders.\n",{"id":2120,"difficulty":208,"q":2121,"a":2122},"context-no-selector-granularity","What is the \"no selector granularity\" limitation of Context?","When you call `useContext(MyContext)`, you subscribe to the **entire\ncontext value**. There is no built-in way to say \"re-render only if\n`state.count` changed\" — you always get the whole object, and any change\nto any part of it triggers a re-render.\n\n```jsx\n\u002F\u002F With Redux + useSelector — granular subscription\nconst count = useSelector(state => state.counter.count)\n\u002F\u002F Only re-renders when state.counter.count changes\n\n\u002F\u002F With Context — no selector, full value\nconst { count, user, cart, theme } = useContext(AppContext)\n\u002F\u002F Re-renders whenever ANY of these change\n```\n\nRedux's `useSelector` uses **strict equality** (or a custom comparator)\non the selector's return value. If `state.counter.count` hasn't changed,\nthe component skips re-rendering even if other parts of the store did.\nLibraries like `use-context-selector` backport this behaviour to Context,\nbut at that point you're carrying the complexity of Redux without its\necosystem.\n\n**Rule of thumb:** If you need subscription granularity finer than \"the\nwhole context value\", Redux (or Zustand\u002FJotai) is the right tool.\n",{"id":2124,"difficulty":121,"q":2125,"a":2126},"zustand-jotai-middle-ground","Where do Zustand and Jotai fit between Context and Redux?","**Zustand** and **Jotai** occupy the pragmatic middle ground: they provide\ngranular subscriptions and minimal boilerplate without Redux's architectural\noverhead.\n\n**Zustand** is a single-store solution. You define state and actions together\nin a `create` call; components subscribe to slices with a selector:\n\n```js\nimport { create } from 'zustand'\n\nconst useStore = create(set => ({\n  count: 0,\n  increment: () => set(state => ({ count: state.count + 1 })),\n}))\n\nfunction Counter() {\n  const count     = useStore(state => state.count)      \u002F\u002F granular — re-renders only when count changes\n  const increment = useStore(state => state.increment)  \u002F\u002F stable reference\n  return \u003Cbutton onClick={increment}>{count}\u003C\u002Fbutton>\n}\n```\n\n**Jotai** uses an **atomic** model: each `atom` is an independent piece of\nstate. Components subscribe to individual atoms, so updates are naturally\nscoped to only the atoms a component reads.\n\nNeither has Redux DevTools baked in (though Zustand has a devtools\nmiddleware) or first-class saga\u002Fthunk support.\n\n**Rule of thumb:** Reach for Zustand when Context starts hurting and Redux\nfeels like overkill; reach for Jotai when you want fine-grained atomic\nstate without a centralised store.\n",{"id":2128,"difficulty":121,"q":2129,"a":2130},"testing-context-vs-redux","How does testing differ between Context-based state and Redux?","**Context** — wrap the component under test in its Provider and pass a\ncontrolled value. No special setup, no mock store.\n\n```jsx\n\u002F\u002F Testing a component that reads from Context\ntest('shows user name', () => {\n  render(\n    \u003CUserContext.Provider value={{ user: { name: 'Ada' } }}>\n      \u003CUserBadge \u002F>\n    \u003C\u002FUserContext.Provider>\n  )\n  expect(screen.getByText('Ada')).toBeInTheDocument()\n})\n```\n\n**Redux** — use RTK's `configureStore` with a real (or pre-populated) store,\nor use `renderWithProviders` from `@reduxjs\u002Ftoolkit\u002Freact`:\n\n```jsx\ntest('increments counter', async () => {\n  const { store } = renderWithProviders(\u003CCounter \u002F>)\n  await userEvent.click(screen.getByRole('button', { name: \u002Fincrement\u002Fi }))\n  expect(store.getState().counter.value).toBe(1)\n})\n```\n\nRedux gives you direct access to `store.getState()` and `store.dispatch()`\nin tests, making it easy to assert against the store as well as the UI.\nContext is simpler to mock but harder to inspect state independently of\nthe rendered output.\n\n**Rule of thumb:** Context tests are simpler to write; Redux tests are more\npowerful for asserting state transitions independently from rendering.\n",{"id":2132,"difficulty":121,"q":2133,"a":2134},"multiple-contexts-vs-one-store","How do multiple small contexts compare to one large Redux store?","**Multiple contexts** let you co-locate state near the component subtrees\nthat use it. Each context has an independent update cycle, so they don't\ninterfere with each other. The cost is coordination: sharing data across\ncontexts requires lifting it to a common ancestor, which can recreate\nprop-drilling at the context level.\n\n```jsx\n\u002F\u002F Fine — independent, non-overlapping concerns\n\u003CAuthContext.Provider value={auth}>\n  \u003CThemeContext.Provider value={theme}>\n    \u003CNotificationsContext.Provider value={notifications}>\n      \u003CApp \u002F>\n    \u003C\u002FNotificationsContext.Provider>\n  \u003C\u002FThemeContext.Provider>\n\u003C\u002FAuthContext.Provider>\n```\n\n**One Redux store** centralises everything. Any slice can read from any\nother slice in a selector. The downside is that everything is global by\ndefault — there's no encapsulation boundary.\n\nRTK's `createSlice` recovers some modularity (each slice is a self-contained\nmodule), but ultimately they all merge into one global state tree.\n\n**Rule of thumb:** Multiple contexts for independent concerns; a Redux store\nwhen slices need to derive state from each other or share middleware.\n",{"id":2136,"difficulty":96,"q":2137,"a":2138},"global-vs-local-state","How should you decide between global state (Context\u002FRedux) and local component state?","A useful mental checklist — before reaching for a global state solution,\nask these questions:\n\n```\nDoes only ONE component need this state?     → useState, keep it local\nDo a FEW co-located components need it?      → lift state to nearest common ancestor\nDo MANY unrelated components need it?        → Context (if infrequent updates)\nIs it updated FREQUENTLY or needs middleware? → Redux \u002F Zustand\nIs it SERVER state (loading, caching)?        → React Query \u002F SWR\n```\n\nOver-globalising state is a common mistake: putting form field values,\nmodal-open flags, and tooltip visibility into Redux adds noise to the\naction log and couples unrelated parts of the app. These are **ephemeral\nUI state** — local `useState` is almost always the right choice.\n\n**Rule of thumb:** Default to local state; promote to global only when\nsharing across unrelated subtrees becomes genuinely painful.\n",{"id":2140,"difficulty":208,"q":2141,"a":2142},"context-ssr-nextjs-gotchas","What are the Context gotchas in SSR \u002F Next.js?","In a **Server Component** world (Next.js App Router), Context Providers\nmust be **Client Components** — they rely on React's runtime reconciler,\nwhich only runs in the browser. Trying to render a Context Provider in a\nServer Component throws an error.\n\n```jsx\n\u002F\u002F app\u002Fproviders.tsx — must be a Client Component\n'use client'\nimport { ThemeContext } from '.\u002FThemeContext'\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n  const [theme, setTheme] = useState('dark')\n  return (\n    \u003CThemeContext.Provider value={{ theme, setTheme }}>\n      {children}\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n\n\u002F\u002F app\u002Flayout.tsx — Server Component that wraps with Providers\nimport { Providers } from '.\u002Fproviders'\nexport default function RootLayout({ children }) {\n  return \u003Chtml>\u003Cbody>\u003CProviders>{children}\u003C\u002FProviders>\u003C\u002Fbody>\u003C\u002Fhtml>\n}\n```\n\nAdditional gotchas:\n- **Hydration mismatch** — if Context initialises from browser-only APIs\n  (localStorage, cookies), the server-rendered HTML will differ from the\n  client hydration pass, causing warnings.\n- **Per-request isolation** — in traditional SSR, a singleton Context\n  module is shared across requests. Each request must create its own\n  Provider instance; never store per-user data in a module-level variable.\n\n**Rule of thumb:** In Next.js App Router, keep Providers in a single\n`'use client'` file at the root layout and pass server-fetched data in\nas props rather than initialising them from async operations inside the\nProvider.\n",{"id":2144,"difficulty":96,"q":2145,"a":2146},"context-vs-redux-summary","How do you summarise the choice between Context and Redux for an interviewer?","A strong interview answer covers three axes: **frequency of updates**,\n**complexity of async logic**, and **team\u002Fscale requirements**.\n\n```\nContext is right when:\n  ✓ State changes infrequently (theme, auth, locale)\n  ✓ No async middleware needed\n  ✓ Small-to-medium app or isolated subtree\n  ✓ You want zero extra dependencies\n\nRedux (RTK) is right when:\n  ✓ State updates frequently or drives complex UI\n  ✓ Async sequences: loading\u002Ferror states, cancellation, polling\n  ✓ Derived data across multiple slices (selectors)\n  ✓ Large team needs enforced conventions\n  ✓ Time-travel debugging has real value\n\nMiddle ground (Zustand\u002FJotai):\n  ✓ Granular subscriptions without Redux boilerplate\n  ✓ Stores need to be shared but you don't want a full Redux setup\n```\n\nInterviewers want to hear that you understand the trade-offs, not just\n\"Context is simpler.\" Mention re-render behaviour, selector granularity,\nand middleware — those are the differentiators that signal real experience.\n\n**Rule of thumb:** Start with local state, promote to Context for ambient\nglobal data, reach for Redux when your state has behaviour (async, derived\ndata, audit trail) that Context can't model cleanly.\n",{"description":94},"Context vs Redux interview questions — when to use each, performance trade-offs, re-render behavior, middleware, devtools, and scaling considerations.","react\u002Fstate-management\u002Fcontext-vs-redux","Context vs Redux","Z9H1j547sQNnxtFWfskeekVWpzfniphJMcA-f2UPPIA",{"id":2153,"title":2154,"body":2155,"description":94,"difficulty":208,"extension":97,"framework":10,"frameworkSlug":8,"meta":2159,"navigation":100,"order":37,"path":2160,"questions":2161,"questionsCount":2214,"related":169,"seo":2215,"seoDescription":2216,"stem":2217,"subtopic":2154,"topic":81,"topicSlug":83,"updated":420,"__hash__":2218},"qa\u002Freact\u002Ftesting\u002Fmocking-async.md","Mocking Async",{"type":91,"value":2156,"toc":2157},[],{"title":94,"searchDepth":11,"depth":11,"links":2158},[],{},"\u002Freact\u002Ftesting\u002Fmocking-async",[2162,2166,2170,2174,2178,2182,2186,2190,2194,2198,2202,2206,2210],{"id":2163,"difficulty":121,"q":2164,"a":2165},"mock-fetch","How do you mock `fetch` in React component tests?","The simplest approach is to replace `global.fetch` with a spy before\neach test and restore it after.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport UserProfile from '.\u002FUserProfile'\n\nbeforeEach(() => {\n  global.fetch = vi.fn()\n})\n\nafterEach(() => {\n  vi.restoreAllMocks()\n})\n\ntest('displays user data after fetch', async () => {\n  global.fetch.mockResolvedValue({\n    ok: true,\n    json: async () => ({ id: 1, name: 'Alice', role: 'admin' }),\n  })\n\n  render(\u003CUserProfile userId={1} \u002F>)\n\n  expect(await screen.findByText('Alice')).toBeInTheDocument()\n  expect(screen.getByText('admin')).toBeInTheDocument()\n  expect(global.fetch).toHaveBeenCalledWith('\u002Fapi\u002Fusers\u002F1')\n})\n\ntest('shows error when fetch fails', async () => {\n  global.fetch.mockResolvedValue({ ok: false, status: 404 })\n\n  render(\u003CUserProfile userId={999} \u002F>)\n\n  expect(await screen.findByText(\u002Fuser not found\u002Fi)).toBeInTheDocument()\n})\n```\n\nThe downside: you're asserting the URL and response shape in every test.\nThis couples tests to the HTTP layer. For larger projects, prefer MSW\n(Mock Service Worker) which intercepts at the network level.\n\n**Rule of thumb:** Use `global.fetch` mocking for quick unit tests;\nswitch to MSW when you have more than a handful of API calls to test.\n",{"id":2167,"difficulty":121,"q":2168,"a":2169},"msw-setup","What is Mock Service Worker (MSW) and how do you set it up with RTL?","**MSW** intercepts outgoing HTTP requests at the service-worker level\n(browser) or the Node.js `http` module level (tests), letting you define\nhandlers that return fake responses — without modifying `fetch` or\n`axios` in your source code.\n\nSetup for Vitest\u002FNode:\n\n**1. Install**\n```bash\nnpm install -D msw\n```\n\n**2. Define handlers (`src\u002Fmocks\u002Fhandlers.ts`)**\n```ts\nimport { http, HttpResponse } from 'msw'\n\nexport const handlers = [\n  http.get('\u002Fapi\u002Fusers\u002F:id', ({ params }) =>\n    HttpResponse.json({ id: params.id, name: 'Alice' })\n  ),\n  http.post('\u002Fapi\u002Flogin', async ({ request }) => {\n    const body = await request.json()\n    if (body.password === 'secret') {\n      return HttpResponse.json({ token: 'abc123' })\n    }\n    return new HttpResponse(null, { status: 401 })\n  }),\n]\n```\n\n**3. Create the server (`src\u002Fmocks\u002Fserver.ts`)**\n```ts\nimport { setupServer } from 'msw\u002Fnode'\nimport { handlers } from '.\u002Fhandlers'\n\nexport const server = setupServer(...handlers)\n```\n\n**4. Wire into test setup**\n```ts\n\u002F\u002F src\u002Ftest\u002Fsetup.ts\nimport { server } from '..\u002Fmocks\u002Fserver'\nimport { beforeAll, afterEach, afterAll } from 'vitest'\n\nbeforeAll(() => server.listen({ onUnhandledRequest: 'error' }))\nafterEach(() => server.resetHandlers())   \u002F\u002F clean up per-test overrides\nafterAll(() => server.close())\n```\n\nNow every test automatically intercepts matching requests without any\nper-test configuration.\n\n**Rule of thumb:** Set `onUnhandledRequest: 'error'` — it forces you to\nhandle every request explicitly, catching forgotten mocks before they\nsilently return `undefined`.\n",{"id":2171,"difficulty":96,"q":2172,"a":2173},"loading-state-test","How do you test loading states in a component that fetches data?","Assert the loading indicator is present *before* the data arrives, then\nassert it's gone once the data loads.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport { server } from '..\u002Fmocks\u002Fserver'\nimport { http, HttpResponse } from 'msw'\nimport UserList from '.\u002FUserList'\n\ntest('shows skeleton while loading, then data', async () => {\n  \u002F\u002F Default handler from server setup will respond eventually\n  render(\u003CUserList \u002F>)\n\n  \u002F\u002F Loading state should appear synchronously\n  expect(screen.getByRole('status', { name: \u002Floading\u002Fi }))\n    .toBeInTheDocument()\n  \u002F\u002F or: expect(screen.getByTestId('skeleton')).toBeInTheDocument()\n\n  \u002F\u002F Data appears after fetch completes\n  expect(await screen.findByText('Alice')).toBeInTheDocument()\n\n  \u002F\u002F Loading indicator is gone\n  expect(screen.queryByRole('status', { name: \u002Floading\u002Fi }))\n    .not.toBeInTheDocument()\n})\n```\n\nFor a slow network simulation, delay the MSW response:\n```js\nimport { delay } from 'msw'\n\nserver.use(\n  http.get('\u002Fapi\u002Fusers', async () => {\n    await delay(500)           \u002F\u002F artificial delay\n    return HttpResponse.json([{ id: 1, name: 'Alice' }])\n  })\n)\n```\n\n**Rule of thumb:** Always assert both the loading state AND the resolved\nstate in the same test — it proves the loading indicator appears and\ndisappears correctly.\n",{"id":2175,"difficulty":96,"q":2176,"a":2177},"error-state-test","How do you test error states from failed API calls?","Override the MSW handler for the specific test to return an error\nresponse, then assert the error UI.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport { server } from '..\u002Fmocks\u002Fserver'\nimport { http, HttpResponse } from 'msw'\nimport UserList from '.\u002FUserList'\n\ntest('shows error message on 500', async () => {\n  server.use(\n    http.get('\u002Fapi\u002Fusers', () => new HttpResponse(null, { status: 500 }))\n  )\n  render(\u003CUserList \u002F>)\n  expect(await screen.findByRole('alert')).toHaveTextContent(\u002Fsomething went wrong\u002Fi)\n})\n\ntest('shows not-found message on 404', async () => {\n  server.use(\n    http.get('\u002Fapi\u002Fusers\u002F999', () => new HttpResponse(null, { status: 404 }))\n  )\n  render(\u003CUserProfile userId={999} \u002F>)\n  expect(await screen.findByText(\u002Fuser not found\u002Fi)).toBeInTheDocument()\n})\n\ntest('shows network error message', async () => {\n  server.use(\n    http.get('\u002Fapi\u002Fusers', () => HttpResponse.error())  \u002F\u002F simulates network failure\n  )\n  render(\u003CUserList \u002F>)\n  expect(await screen.findByText(\u002Fnetwork error\u002Fi)).toBeInTheDocument()\n})\n```\n\n`server.resetHandlers()` in `afterEach` ensures these one-off overrides\ndon't bleed into other tests.\n\n**Rule of thumb:** Test at least three HTTP outcomes: success, known error\n(400\u002F404), and unexpected server error (500). Each maps to a different\nuser-facing message.\n",{"id":2179,"difficulty":208,"q":2180,"a":2181},"fake-timers","How do you test components that use `setTimeout` or `setInterval`?","Use Vitest's (or Jest's) fake timer API to control time without actually\nwaiting.\n\n```js\nimport { render, screen, act } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\n\nfunction Countdown({ seconds }) {\n  const [remaining, setRemaining] = React.useState(seconds)\n  React.useEffect(() => {\n    const id = setInterval(() => setRemaining(r => r - 1), 1000)\n    return () => clearInterval(id)\n  }, [])\n  return \u003Cp>{remaining}s remaining\u003C\u002Fp>\n}\n\ndescribe('Countdown', () => {\n  beforeEach(() => vi.useFakeTimers())\n  afterEach(() => vi.useRealTimers())\n\n  test('decrements every second', () => {\n    render(\u003CCountdown seconds={5} \u002F>)\n    expect(screen.getByText('5s remaining')).toBeInTheDocument()\n\n    act(() => vi.advanceTimersByTime(1000))\n    expect(screen.getByText('4s remaining')).toBeInTheDocument()\n\n    act(() => vi.advanceTimersByTime(3000))\n    expect(screen.getByText('1s remaining')).toBeInTheDocument()\n  })\n\n  test('cleans up interval on unmount', () => {\n    const clearIntervalSpy = vi.spyOn(global, 'clearInterval')\n    const { unmount } = render(\u003CCountdown seconds={3} \u002F>)\n    unmount()\n    expect(clearIntervalSpy).toHaveBeenCalled()\n  })\n})\n```\n\nWrapping `vi.advanceTimersByTime` in `act()` flushes any resulting React\nstate updates synchronously.\n\n**Rule of thumb:** Always call `vi.useRealTimers()` in `afterEach` —\nfake timers left running can cause cascading failures in later tests.\n",{"id":2183,"difficulty":121,"q":2184,"a":2185},"module-mocking","How do you mock an entire module (e.g., an API client) in Vitest?","Use `vi.mock()` at the top of the test file (Vitest hoists it before\nimports). Then configure specific implementations with `vi.mocked()`.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport * as api from '..\u002Fapi\u002FuserApi'   \u002F\u002F named imports\nimport UserProfile from '.\u002FUserProfile'\n\nvi.mock('..\u002Fapi\u002FuserApi')   \u002F\u002F auto-mocks all exports to vi.fn()\n\nconst mockedApi = vi.mocked(api)\n\nbeforeEach(() => {\n  mockedApi.fetchUser.mockResolvedValue({ id: 1, name: 'Alice' })\n})\n\nafterEach(() => vi.clearAllMocks())\n\ntest('fetches and displays user', async () => {\n  render(\u003CUserProfile userId={1} \u002F>)\n  expect(await screen.findByText('Alice')).toBeInTheDocument()\n  expect(mockedApi.fetchUser).toHaveBeenCalledWith(1)\n})\n\ntest('shows error when fetch throws', async () => {\n  mockedApi.fetchUser.mockRejectedValue(new Error('Network error'))\n  render(\u003CUserProfile userId={1} \u002F>)\n  expect(await screen.findByText(\u002Fnetwork error\u002Fi)).toBeInTheDocument()\n})\n```\n\nFor a partial mock (keep some real implementations):\n```js\nvi.mock('..\u002Futils\u002Fdate', async (importOriginal) => {\n  const real = await importOriginal()\n  return { ...real, formatDate: vi.fn().mockReturnValue('Jan 1') }\n})\n```\n\n**Rule of thumb:** Prefer MSW for HTTP mocking; use `vi.mock()` for\nnon-HTTP modules (analytics SDKs, third-party clients, browser APIs).\n",{"id":2187,"difficulty":208,"q":2188,"a":2189},"react-query-testing","How do you test components that use React Query (`useQuery`, `useMutation`)?","Create a fresh `QueryClient` per test with `retry: false` (so failed\nqueries don't retry in tests), wrap the component in a\n`QueryClientProvider`, and let MSW handle the HTTP responses.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { server } from '..\u002Fmocks\u002Fserver'\nimport { http, HttpResponse } from 'msw'\nimport UserList from '.\u002FUserList'\n\nfunction createTestClient() {\n  return new QueryClient({\n    defaultOptions: {\n      queries: { retry: false },    \u002F\u002F don't retry on error in tests\n      mutations: { retry: false },\n    },\n  })\n}\n\nfunction renderWithQuery(ui) {\n  const client = createTestClient()\n  return render(\n    \u003CQueryClientProvider client={client}>{ui}\u003C\u002FQueryClientProvider>\n  )\n}\n\ntest('renders user list from query', async () => {\n  server.use(\n    http.get('\u002Fapi\u002Fusers', () =>\n      HttpResponse.json([{ id: 1, name: 'Alice' }])\n    )\n  )\n  renderWithQuery(\u003CUserList \u002F>)\n  expect(await screen.findByText('Alice')).toBeInTheDocument()\n})\n\ntest('mutation updates UI after success', async () => {\n  const user = userEvent.setup()\n  server.use(\n    http.get('\u002Fapi\u002Ftodos', () => HttpResponse.json([])),\n    http.post('\u002Fapi\u002Ftodos', () => HttpResponse.json({ id: 1, text: 'New todo' }))\n  )\n  renderWithQuery(\u003CTodoApp \u002F>)\n  await user.type(screen.getByRole('textbox'), 'New todo')\n  await user.click(screen.getByRole('button', { name: \u002Fadd\u002Fi }))\n  expect(await screen.findByText('New todo')).toBeInTheDocument()\n})\n```\n\n**Rule of thumb:** Always use a fresh `QueryClient` per test — a shared\nclient caches query results across tests and causes false passes or\nunexplained failures.\n",{"id":2191,"difficulty":208,"q":2192,"a":2193},"debounce-throttle-test","How do you test debounced or throttled functions inside components?","Use fake timers to control the debounce delay without real waiting.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { vi } from 'vitest'\nimport SearchBox from '.\u002FSearchBox'  \u002F\u002F internally debounces onSearch by 300ms\n\ndescribe('SearchBox debounce', () => {\n  beforeEach(() => vi.useFakeTimers())\n  afterEach(() => vi.useRealTimers())\n\n  test('calls onSearch only after debounce delay', async () => {\n    const onSearch = vi.fn()\n    const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime })\n    render(\u003CSearchBox onSearch={onSearch} \u002F>)\n\n    await user.type(screen.getByRole('textbox'), 'react')\n\n    \u002F\u002F Before debounce fires — should not have been called\n    expect(onSearch).not.toHaveBeenCalled()\n\n    \u002F\u002F Advance time past debounce window\n    vi.runAllTimers()\n\n    expect(onSearch).toHaveBeenCalledTimes(1)\n    expect(onSearch).toHaveBeenCalledWith('react')\n  })\n\n  test('debounces rapid typing to a single call', async () => {\n    const onSearch = vi.fn()\n    const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime })\n    render(\u003CSearchBox onSearch={onSearch} \u002F>)\n\n    await user.type(screen.getByRole('textbox'), 'react')\n    await user.clear(screen.getByRole('textbox'))\n    await user.type(screen.getByRole('textbox'), 'hooks')\n\n    vi.runAllTimers()\n\n    \u002F\u002F Only the final value triggers the callback\n    expect(onSearch).toHaveBeenCalledTimes(1)\n    expect(onSearch).toHaveBeenCalledWith('hooks')\n  })\n})\n```\n\nKey: pass `{ advanceTimers: vi.advanceTimersByTime }` to `userEvent.setup()`\nso userEvent's internal delays use fake timers too.\n\n**Rule of thumb:** Always pair fake timers with `userEvent.setup({ advanceTimers })`\n— without it, userEvent's own async delays break fake-timer tests.\n",{"id":2195,"difficulty":121,"q":2196,"a":2197},"localstorage-mock","How do you mock `localStorage` in tests?","The jsdom environment (used by Vitest\u002FJest with RTL) provides a real\n`localStorage` implementation, but it persists between tests unless\ncleared. The cleanest approach is to clear it after each test.\n\n```js\nafterEach(() => localStorage.clear())\n\ntest('persists theme preference to localStorage', async () => {\n  const user = userEvent.setup()\n  render(\u003CThemeToggle \u002F>)\n\n  await user.click(screen.getByRole('button', { name: \u002Fdark mode\u002Fi }))\n\n  expect(localStorage.getItem('theme')).toBe('dark')\n})\n\ntest('reads theme from localStorage on mount', () => {\n  localStorage.setItem('theme', 'dark')\n  render(\u003CThemeToggle \u002F>)\n  expect(screen.getByRole('button', { name: \u002Flight mode\u002Fi })).toBeInTheDocument()\n})\n```\n\nIf you need to test a case where `localStorage` is unavailable (private\nbrowsing), mock it with a spy:\n```js\nvi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {\n  throw new DOMException('QuotaExceededError')\n})\n```\n\n**Rule of thumb:** Call `localStorage.clear()` in `afterEach`, not\n`beforeEach` — clearing before masks cleanup bugs from prior tests.\n",{"id":2199,"difficulty":121,"q":2200,"a":2201},"async-act-warning","What does the \"act() warning\" mean and how do you fix it?","The `act()` warning appears when a state update happens *outside* of a\nReact `act()` call, meaning React's test utilities weren't notified that\nstate was about to change.\n\n```\nWarning: An update to Counter inside a test was not wrapped in act(...)\n```\n\n**Common causes and fixes:**\n\n**1. Async state update not awaited:**\n```js\n\u002F\u002F ❌ State update from async callback is not awaited\nfireEvent.click(button)\n\u002F\u002F The click triggers a fetch that updates state after the test ends\n\n\u002F\u002F ✅ Use findBy* which internally wraps in act\nawait screen.findByText('Loaded')\n```\n\n**2. Missing `await` on userEvent:**\n```js\n\u002F\u002F ❌\nuser.click(button)   \u002F\u002F forgot await\n\n\u002F\u002F ✅\nawait user.click(button)\n```\n\n**3. `useEffect` with a timer not cleaned up:**\n```js\n\u002F\u002F ❌ Timer fires after test cleanup\nReact.useEffect(() => {\n  setTimeout(() => setState('done'), 500)\n}, [])\n\n\u002F\u002F ✅ Cancel the timer in cleanup\nReact.useEffect(() => {\n  const id = setTimeout(() => setState('done'), 500)\n  return () => clearTimeout(id)\n}, [])\n```\n\n**4. Manually wrapping needed:**\n```js\nact(() => {\n  \u002F\u002F trigger some event that causes synchronous state updates\n  eventEmitter.emit('update', newData)\n})\n```\n\n**Rule of thumb:** The warning is always a signal that a state update\nescaped test control — find and `await` the async operation that triggered\nit.\n",{"id":2203,"difficulty":96,"q":2204,"a":2205},"spy-console","How do you test that a component logs errors or warnings without polluting test output?","Spy on `console.error` or `console.warn` with `vi.spyOn()`, suppress the\noutput by replacing the implementation, assert the call, then restore.\n\n```js\nimport { vi } from 'vitest'\n\ntest('logs validation error for invalid prop', () => {\n  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\n\n  render(\u003CDatePicker value=\"not-a-date\" \u002F>)\n\n  expect(consoleSpy).toHaveBeenCalledWith(\n    expect.stringContaining('Invalid date format')\n  )\n\n  consoleSpy.mockRestore()\n})\n```\n\nFor error boundaries that log the error:\n```js\ntest('error boundary shows fallback and logs', () => {\n  const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\n\n  render(\n    \u003CErrorBoundary>\n      \u003CThrowingComponent \u002F>\n    \u003C\u002FErrorBoundary>\n  )\n\n  expect(screen.getByText(\u002Fsomething went wrong\u002Fi)).toBeInTheDocument()\n  expect(errorSpy).toHaveBeenCalled()\n\n  errorSpy.mockRestore()\n})\n```\n\nIf you want to assert `console.error` is NOT called (no unexpected\nerrors):\n```js\nconst consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\nrender(\u003CCleanComponent \u002F>)\nexpect(consoleSpy).not.toHaveBeenCalled()\nconsoleSpy.mockRestore()\n```\n\n**Rule of thumb:** Always `mockRestore()` after spy assertions — leaving\nthe spy in place will swallow real errors in subsequent tests.\n",{"id":2207,"difficulty":208,"q":2208,"a":2209},"polling-test","How do you test a component that polls an API at a regular interval?","Combine fake timers with MSW to control both the clock and the server\nresponses. Advance time in steps and assert updated data between steps.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport { act } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport { server } from '..\u002Fmocks\u002Fserver'\nimport { http, HttpResponse } from 'msw'\nimport LivePriceWidget from '.\u002FLivePriceWidget'  \u002F\u002F polls \u002Fapi\u002Fprice every 5s\n\ndescribe('LivePriceWidget', () => {\n  beforeEach(() => vi.useFakeTimers())\n  afterEach(() => {\n    vi.useRealTimers()\n    server.resetHandlers()\n  })\n\n  test('updates price after each poll interval', async () => {\n    let price = 100\n\n    server.use(\n      http.get('\u002Fapi\u002Fprice', () => HttpResponse.json({ value: price }))\n    )\n\n    render(\u003CLivePriceWidget \u002F>)\n    expect(await screen.findByText('$100')).toBeInTheDocument()\n\n    \u002F\u002F Simulate a price change on the server\n    price = 150\n    await act(async () => {\n      vi.advanceTimersByTime(5000)  \u002F\u002F trigger next poll\n    })\n\n    expect(await screen.findByText('$150')).toBeInTheDocument()\n  })\n})\n```\n\nThe `await act(async () => { ... })` pattern flushes both the timer\ncallback and the resulting async state update.\n\n**Rule of thumb:** For polling tests, wrap `vi.advanceTimersByTime`\nin `await act(async () => { ... })` to ensure React processes both the\ntimer expiry and the subsequent promise resolution.\n",{"id":2211,"difficulty":208,"q":2212,"a":2213},"race-condition-test","How do you test that a component handles race conditions — e.g., only showing the result of the latest request?","Use MSW handlers with controlled response ordering (via `delay`) to\nsimulate an older request resolving after a newer one.\n\n```js\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { server } from '..\u002Fmocks\u002Fserver'\nimport { http, HttpResponse, delay } from 'msw'\nimport SearchResults from '.\u002FSearchResults'\n\ntest('shows result of latest search, ignoring stale responses', async () => {\n  let requestCount = 0\n\n  server.use(\n    http.get('\u002Fapi\u002Fsearch', async ({ request }) => {\n      const q = new URL(request.url).searchParams.get('q')\n      requestCount++\n\n      if (requestCount === 1) {\n        \u002F\u002F First request (stale) — slow response\n        await delay(500)\n        return HttpResponse.json({ query: q, results: ['Stale result'] })\n      }\n      \u002F\u002F Second request (latest) — fast response\n      return HttpResponse.json({ query: q, results: ['Fresh result'] })\n    })\n  )\n\n  const user = userEvent.setup()\n  render(\u003CSearchResults \u002F>)\n\n  \u002F\u002F Type first query, then quickly change it\n  await user.type(screen.getByRole('textbox'), 'slow')\n  await user.clear(screen.getByRole('textbox'))\n  await user.type(screen.getByRole('textbox'), 'fast')\n\n  \u002F\u002F Should show fresh result only\n  expect(await screen.findByText('Fresh result')).toBeInTheDocument()\n  expect(screen.queryByText('Stale result')).not.toBeInTheDocument()\n})\n```\n\nThis test verifies the component cancels or ignores stale requests\n(typically via `AbortController` or a flag in `useEffect`).\n\n**Rule of thumb:** Test race conditions by controlling response timing\nin MSW handlers; if the test passes even without abort logic, the\ntiming simulation isn't strict enough.\n",13,{"description":94},"React async testing interview questions — mocking fetch, MSW setup, testing loading and error states, fake timers, vi.mock, React Query testing, debounce\u002Fthrottle, and localStorage mocking.","react\u002Ftesting\u002Fmocking-async","4qnBXsczPivanIvp--w3X85AcXTAYLr32BCFDvmr2qo",{"id":2220,"title":2221,"body":2222,"description":94,"difficulty":96,"extension":97,"framework":10,"frameworkSlug":8,"meta":2226,"navigation":100,"order":46,"path":2228,"questions":2229,"questionsCount":493,"related":169,"seo":2290,"seoDescription":2291,"stem":2292,"subtopic":2221,"topic":28,"topicSlug":29,"updated":174,"__hash__":2293},"qa\u002Freact\u002Fcomponents\u002Fconditional-rendering.md","Conditional Rendering",{"type":91,"value":2223,"toc":2224},[],{"title":94,"searchDepth":11,"depth":11,"links":2225},[],{"subtopicSlug":2227},"conditional-rendering","\u002Freact\u002Fcomponents\u002Fconditional-rendering",[2230,2234,2238,2242,2246,2250,2254,2258,2262,2266,2270,2274,2278,2282,2286],{"id":2231,"difficulty":96,"q":2232,"a":2233},"ways-to-conditionally-render","What are the main ways to conditionally render content in React?","React has four idiomatic patterns:\n\n```jsx\n\u002F\u002F 1. Logical && — render right side only when left is truthy\n{isLoggedIn && \u003CDashboard \u002F>}\n\n\u002F\u002F 2. Ternary — render one of two options\n{isLoggedIn ? \u003CDashboard \u002F> : \u003CLogin \u002F>}\n\n\u002F\u002F 3. Variable \u002F if-else above return\nlet content = isLoggedIn ? \u003CDashboard \u002F> : \u003CLogin \u002F>\nreturn \u003Cmain>{content}\u003C\u002Fmain>\n\n\u002F\u002F 4. Early return — bail out of the component entirely\nif (!isLoggedIn) return \u003CLogin \u002F>\nreturn \u003CDashboard \u002F>\n```\n\nEach has its place:\n- `&&` — one branch only, very readable when the condition is simple.\n- Ternary — exactly two branches.\n- Variable \u002F if-else — complex conditions with more than two outcomes.\n- Early return — guard clause at the top when the component shouldn't\n  render at all in certain states.\n\n**Rule of thumb:** use the simplest form that's still readable. If a\nternary has nested ternaries, it's time for an if-else or a variable.\n",{"id":2235,"difficulty":96,"q":2236,"a":2237},"and-operator","How does the && operator work for conditional rendering?","JavaScript's `&&` returns the **last evaluated operand** — not necessarily\na boolean. React renders whatever value appears on the right side of `&&`\nwhen the left side is truthy.\n\n```jsx\nconst count = 5\nreturn (\n  \u003Cdiv>\n    {count > 0 && \u003CBadge count={count} \u002F>}\n    {\u002F* → renders \u003CBadge \u002F> because (count > 0) is true *\u002F}\n  \u003C\u002Fdiv>\n)\n```\n\nWhen the condition is `false`, `&&` short-circuits and returns `false`.\nReact treats `false` as \"render nothing\" — no output.\n\n**Rule of thumb:** `{condition && \u003CElement \u002F>}` is idiomatic React. The\ncondition should be a boolean; if in doubt, cast it: `{!!value && \u003CX \u002F>}`.\n",{"id":2239,"difficulty":121,"q":2240,"a":2241},"falsy-zero-bug","What is the \"falsy zero\" bug with the && operator?","`&&` returns the **left operand** when it is falsy. If that operand is `0`,\nReact renders the number `0` (since `0` is a valid renderable value).\n\n```jsx\nconst messages = []    \u002F\u002F length is 0\n\n\u002F\u002F ❌ Renders \"0\" when messages is empty\nreturn \u003Cdiv>{messages.length && \u003CMessageList items={messages} \u002F>}\u003C\u002Fdiv>\n\n\u002F\u002F ✅ Force the left side to a boolean\nreturn \u003Cdiv>{messages.length > 0 && \u003CMessageList items={messages} \u002F>}\u003C\u002Fdiv>\n\n\u002F\u002F ✅ Or use Boolean()\nreturn \u003Cdiv>{Boolean(messages.length) && \u003CMessageList items={messages} \u002F>}\u003C\u002Fdiv>\n\n\u002F\u002F ✅ Or use a ternary\nreturn \u003Cdiv>{messages.length ? \u003CMessageList items={messages} \u002F> : null}\u003C\u002Fdiv>\n```\n\n**Rule of thumb:** never use `count &&` or `array.length &&` directly.\nAlways compare: `count > 0 &&` or `array.length > 0 &&`. The same trap\nexists for any number that could be `0`.\n",{"id":2243,"difficulty":96,"q":2244,"a":2245},"ternary-rendering","How do you use a ternary for conditional rendering?","The ternary expression (`condition ? a : b`) is a JavaScript expression and\nis legal inside JSX `{}`. It's the idiomatic way to render one of two\nalternatives.\n\n```jsx\nfunction AuthStatus({ isLoggedIn, username }) {\n  return (\n    \u003Cheader>\n      {isLoggedIn\n        ? \u003Cspan>Welcome, {username}!\u003C\u002Fspan>\n        : \u003Ca href=\"\u002Flogin\">Sign in\u003C\u002Fa>\n      }\n    \u003C\u002Fheader>\n  )\n}\n```\n\nUse `null` for the false branch when you want to render nothing:\n\n```jsx\n{isAdmin ? \u003CAdminPanel \u002F> : null}\n\u002F\u002F Equivalent to:\n{isAdmin && \u003CAdminPanel \u002F>}\n```\n\n**Rule of thumb:** ternaries are great for exactly two outcomes. For more\nthan two, a variable or a helper function is cleaner.\n",{"id":2247,"difficulty":121,"q":2248,"a":2249},"when-to-use-which","When should you use if\u002Felse, ternary, or && for conditional rendering?","Choose based on the number of outcomes and complexity:\n\n| Pattern | Best for |\n|---------|----------|\n| `&&` | Single optional element; condition is a simple boolean |\n| Ternary | Exactly two alternatives; fits on one or two lines |\n| `if\u002Felse` above return | Multiple alternatives; complex conditions; shared logic before branching |\n| Early `return` | Guard clause — component has nothing to show in this state |\n\n```jsx\n\u002F\u002F && — simple optional\n{hasError && \u003CErrorBanner message={error} \u002F>}\n\n\u002F\u002F Ternary — two outcomes\n{loading ? \u003CSpinner \u002F> : \u003CContent data={data} \u002F>}\n\n\u002F\u002F if\u002Felse — three outcomes\nlet body\nif (loading) body = \u003CSpinner \u002F>\nelse if (error) body = \u003CError message={error} \u002F>\nelse body = \u003CContent data={data} \u002F>\nreturn \u003Cmain>{body}\u003C\u002Fmain>\n\n\u002F\u002F Early return — guard clause\nif (!user) return null\nreturn \u003CProfile user={user} \u002F>\n```\n\n**Rule of thumb:** default to simplicity. Reach for `&&` first, ternary\nfor two branches, if\u002Felse for three or more. Never nest ternaries more\nthan one level deep.\n",{"id":2251,"difficulty":96,"q":2252,"a":2253},"returning-null","How do you render nothing from a component?","Return `null`. React renders nothing to the DOM but the component remains\nmounted — its `useEffect` hooks still run and refs still attach.\n\n```jsx\nfunction Tooltip({ visible, text }) {\n  if (!visible) return null\n  return \u003Cdiv className=\"tooltip\">{text}\u003C\u002Fdiv>\n}\n```\n\nOther falsy values behave differently:\n- `undefined` — React 18+ allows returning `undefined` from a component\n  (was an error in older versions). Treated like `null`.\n- `false` — valid child; renders nothing.\n- `0` — renders the digit `0`. Careful!\n- Empty string `''` — renders nothing visible but creates a text node.\n\n**Rule of thumb:** return `null` (not `false` or `undefined`) when you\nwant a component to render nothing — it's explicit and unambiguous.\n",{"id":2255,"difficulty":121,"q":2256,"a":2257},"display-none-vs-conditional","What is the difference between CSS display:none and conditional rendering?","- **CSS `display:none`** — the element is in the DOM, painted, and React's\n  tree, but invisible. State, refs, effects, and event listeners are all live.\n- **Conditional rendering** — the element is not in the DOM. When the\n  condition becomes false, React unmounts the component: state is reset,\n  effects clean up, and refs detach.\n\n```jsx\n\u002F\u002F CSS approach — DOM node always present, just hidden\n\u003Cdiv style={{ display: isVisible ? 'block' : 'none' }}>\n  \u003CExpensiveWidget \u002F>   {\u002F* still mounted, effects running *\u002F}\n\u003C\u002Fdiv>\n\n\u002F\u002F Conditional — component unmounts when isVisible is false\n{isVisible && \u003CExpensiveWidget \u002F>}\n```\n\nUse cases:\n- Prefer conditional rendering when you want fresh state on each appearance.\n- Prefer `display:none` when mounting\u002Funmounting is expensive (e.g. a heavy\n  widget) or when you must preserve internal state across hides.\n\n**Rule of thumb:** default to conditional rendering. Use CSS visibility only\nwhen remounting is measurably expensive or state preservation is required.\n",{"id":2259,"difficulty":96,"q":2260,"a":2261},"conditional-class","How do you conditionally apply a CSS class in JSX?","Use a ternary or template literal inside `className`:\n\n```jsx\n\u002F\u002F Ternary — toggle between two class names\n\u003Cbutton className={isActive ? 'btn btn-active' : 'btn'}>Click\u003C\u002Fbutton>\n\n\u002F\u002F Template literal — build up from base + optional modifier\n\u003Cdiv className={`card ${isHighlighted ? 'card-highlighted' : ''}`}>…\u003C\u002Fdiv>\n\n\u002F\u002F For multiple conditional classes, clsx\u002Fclassnames library is cleaner\nimport clsx from 'clsx'\n\u003Cdiv className={clsx('card', { 'card-highlighted': isHighlighted, 'card-error': hasError })}>\n  …\n\u003C\u002Fdiv>\n```\n\nAvoid complex string concatenation inline — it becomes unreadable quickly.\n\n**Rule of thumb:** for more than one or two conditional classes, add `clsx`\n(or `classnames`) to the project — it handles undefined\u002Ffalse values\ncleanly and the API is easy to scan.\n",{"id":2263,"difficulty":121,"q":2264,"a":2265},"role-based-rendering","How do you render different content based on user role or permission?","Extract the guard logic into a dedicated component or hook to keep the\nrendering logic clean.\n\n```jsx\n\u002F\u002F Simple inline\n{user.role === 'admin' && \u003CAdminPanel \u002F>}\n\n\u002F\u002F Reusable gate component\nfunction Can({ role, children }) {\n  const { user } = useAuth()\n  return user.role === role ? children : null\n}\n\n\u002F\u002F Usage\n\u003CCan role=\"admin\">\n  \u003CDeleteButton \u002F>\n\u003C\u002FCan>\n\n\u002F\u002F Or a hook\nfunction usePermission(requiredRole) {\n  const { user } = useAuth()\n  return user.role === requiredRole\n}\n\nconst canDelete = usePermission('admin')\n{canDelete && \u003CDeleteButton \u002F>}\n```\n\n**Rule of thumb:** keep auth\u002Fpermission logic out of individual components.\nA `\u003CCan>` component or `usePermission` hook keeps it testable and reusable\nacross the app.\n",{"id":2267,"difficulty":121,"q":2268,"a":2269},"switch-rendering","Can you use a switch statement for conditional rendering?","Yes, but a switch statement can't go directly inside JSX. Extract it into\na helper function or variable.\n\n```jsx\nfunction StatusBadge({ status }) {\n  function renderBadge() {\n    switch (status) {\n      case 'success': return \u003Cspan className=\"badge green\">Success\u003C\u002Fspan>\n      case 'error':   return \u003Cspan className=\"badge red\">Error\u003C\u002Fspan>\n      case 'loading': return \u003CSpinner \u002F>\n      default:        return null\n    }\n  }\n\n  return \u003Cdiv className=\"status\">{renderBadge()}\u003C\u002Fdiv>\n}\n```\n\nAn object map is often cleaner than switch for this pattern:\n\n```jsx\nconst BADGE = {\n  success: \u003Cspan className=\"badge green\">Success\u003C\u002Fspan>,\n  error:   \u003Cspan className=\"badge red\">Error\u003C\u002Fspan>,\n  loading: \u003CSpinner \u002F>,\n}\n\nreturn \u003Cdiv>{BADGE[status] ?? null}\u003C\u002Fdiv>\n```\n\n**Rule of thumb:** object maps are more idiomatic React than switch\nstatements for rendering; switch is fine when you need fallthrough or `break`\nbehavior.\n",{"id":2271,"difficulty":121,"q":2272,"a":2273},"avoid-nested-ternaries","How do you avoid deeply nested ternaries in JSX?","Extract the logic to a variable, early return, or helper function before\nthe `return`.\n\n```jsx\n\u002F\u002F ❌ Nested ternary — hard to read\nreturn (\n  \u003Cdiv>\n    {loading\n      ? \u003CSpinner \u002F>\n      : error\n        ? \u003CError message={error} \u002F>\n        : data\n          ? \u003CDataView data={data} \u002F>\n          : \u003CEmpty \u002F>}\n  \u003C\u002Fdiv>\n)\n\n\u002F\u002F ✅ Variable approach\nlet content\nif (loading)     content = \u003CSpinner \u002F>\nelse if (error)  content = \u003CError message={error} \u002F>\nelse if (data)   content = \u003CDataView data={data} \u002F>\nelse             content = \u003CEmpty \u002F>\n\nreturn \u003Cdiv>{content}\u003C\u002Fdiv>\n\n\u002F\u002F ✅ Helper component\nfunction AsyncContent({ loading, error, data }) {\n  if (loading) return \u003CSpinner \u002F>\n  if (error)   return \u003CError message={error} \u002F>\n  if (!data)   return \u003CEmpty \u002F>\n  return \u003CDataView data={data} \u002F>\n}\n```\n\n**Rule of thumb:** if a ternary needs to be nested, refactor to if\u002Felse\nor extract a component. A long chain of nested ternaries is a refactoring\nsignal, not a clever trick.\n",{"id":2275,"difficulty":121,"q":2276,"a":2277},"loading-error-states","What is the pattern for handling loading and error states in rendering?","The guard-clause \u002F early-return pattern keeps the happy path last:\n\n```jsx\nfunction UserProfile({ userId }) {\n  const { data: user, loading, error } = useUser(userId)\n\n  if (loading) return \u003CSkeleton \u002F>\n  if (error)   return \u003CErrorMessage error={error} \u002F>\n  if (!user)   return null\n\n  \u002F\u002F Happy path — no nesting, no clutter\n  return (\n    \u003Carticle>\n      \u003Ch1>{user.name}\u003C\u002Fh1>\n      \u003Cp>{user.bio}\u003C\u002Fp>\n    \u003C\u002Farticle>\n  )\n}\n```\n\nThis pattern is sometimes called \"bail early, render late.\" Each guard\nhandles one sad path and returns, leaving the main render clean.\n\n**Rule of thumb:** load state first, error state second, empty\u002Fnull state\nthird, happy path last. This order prevents accidentally rendering\nundefined properties.\n",{"id":2279,"difficulty":96,"q":2280,"a":2281},"short-circuit-evaluation","What is short-circuit evaluation and how does it apply to conditional rendering?","Short-circuit evaluation means JavaScript stops evaluating an expression as\nsoon as the result is determined:\n- In `A && B` — if `A` is falsy, `B` is never evaluated.\n- In `A || B` — if `A` is truthy, `B` is never evaluated.\n\nReact leverages `&&` for conditional rendering because when the condition\nis false, the right-hand component is not evaluated (and thus not rendered):\n\n```jsx\n\u002F\u002F UserMenu is never evaluated when isLoggedIn is false\n{isLoggedIn && \u003CUserMenu user={user} \u002F>}\n\n\u002F\u002F Useful for expensive renders or components that crash on missing data\n{user && user.isAdmin && \u003CAdminPanel \u002F>}\n```\n\n**Rule of thumb:** short-circuit with `&&` when you have one optional\nelement and no fallback. Use `||` for default values, not for conditional\nrendering — `{value || \u003CFallback \u002F>}` renders `\u003CFallback \u002F>` whenever\n`value` is falsy (including `0` and `''`).\n",{"id":2283,"difficulty":121,"q":2284,"a":2285},"nullish-coalescing","What is the nullish coalescing operator and when is it useful in JSX?","`??` returns the right operand only when the left is `null` or `undefined`\n(not for `0`, `''`, or `false`). This makes it safer than `||` for default\nvalues.\n\n```jsx\n\u002F\u002F || treats 0 and '' as falsy — may swallow valid values\n\u003Cp>Score: {score || 'No score'}\u003C\u002Fp>\n\u002F\u002F If score is 0, renders \"No score\" — wrong!\n\n\u002F\u002F ?? treats only null\u002Fundefined as \"missing\"\n\u003Cp>Score: {score ?? 'No score'}\u003C\u002Fp>\n\u002F\u002F If score is 0, renders \"0\" — correct\n\n\u002F\u002F Common in JSX for optional labels, counts, or display values\n\u003Ch2>{user.displayName ?? user.email}\u003C\u002Fh2>\n```\n\n**Rule of thumb:** use `??` for \"provide a fallback when the value is\nmissing.\" Use `||` only when you intentionally want `0`, `false`, and `''`\nto also trigger the fallback.\n",{"id":2287,"difficulty":208,"q":2288,"a":2289},"suspense-lazy-conditional","How do React.lazy and Suspense relate to conditional rendering?","`React.lazy` lets you load a component's code on demand. `Suspense`\nprovides a fallback to show while the lazy component loads — effectively\na built-in conditional-rendering pattern for async code splitting.\n\n```jsx\nimport { lazy, Suspense } from 'react'\n\nconst AdminPanel = lazy(() => import('.\u002FAdminPanel'))\n\nfunction App({ user }) {\n  return (\n    \u003CSuspense fallback={\u003CSpinner \u002F>}>\n      {user.isAdmin && \u003CAdminPanel \u002F>}\n    \u003C\u002FSuspense>\n  )\n}\n```\n\nWhen `\u003CAdminPanel \u002F>` is first rendered, React suspends, the nearest\n`\u003CSuspense>` shows `\u003CSpinner \u002F>`, and the bundle chunk loads in the\nbackground. On completion React retries the render and shows the panel.\n\n**Rule of thumb:** combine `lazy` + `Suspense` with normal conditional\nrendering. `Suspense` handles the *loading* state of the import; your\n`&&` or ternary handles the *permission* or *visibility* condition.\n",{"description":94},"React conditional rendering interview questions — ternary, &&, null, guard clauses, loading states, CSS display vs conditional mounting, and clean multi-condition patterns.","react\u002Fcomponents\u002Fconditional-rendering","rh-A8_S8gjIU82IuQOu1zQklJ_cBcEkCZg5aTFr7yCQ",{"id":2295,"title":2296,"body":2297,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":2301,"navigation":100,"order":46,"path":2302,"questions":2303,"questionsCount":168,"related":169,"seo":2368,"seoDescription":2369,"stem":2370,"subtopic":2371,"topic":20,"topicSlug":21,"updated":174,"__hash__":2372},"qa\u002Freact\u002Fhooks\u002Fusereducer.md","Usereducer",{"type":91,"value":2298,"toc":2299},[],{"title":94,"searchDepth":11,"depth":11,"links":2300},[],{},"\u002Freact\u002Fhooks\u002Fusereducer",[2304,2308,2312,2316,2320,2324,2328,2332,2336,2340,2344,2348,2352,2356,2360,2364],{"id":2305,"difficulty":96,"q":2306,"a":2307},"what-is-usereducer","What does useReducer return?","`useReducer` returns a pair: the **current state** and a **dispatch function**.\nYou dispatch action objects to the reducer, which computes and returns the next\nstate. React then re-renders with that new state.\n\n```jsx\nconst [state, dispatch] = useReducer(reducer, initialState)\ndispatch({ type: 'increment' })\n```\n\nThe `reducer` is a pure function `(state, action) => nextState`. The\n`initialState` is used on the first render. This pattern mirrors the way Redux\nworks but lives entirely inside a single component (or shared via context).\n",{"id":2309,"difficulty":96,"q":2310,"a":2311},"reducer-function-shape","What is the shape of a reducer function?","A reducer takes the **current state** and an **action** and returns the **next\nstate**. It must be **pure** — no side effects, no mutating the state argument,\nsame inputs always produce the same output.\n\n```jsx\nfunction counterReducer(state, action) {\n  switch (action.type) {\n    case 'increment': return { count: state.count + 1 }\n    case 'decrement': return { count: state.count - 1 }\n    case 'reset':     return { count: 0 }\n    default:          return state \u002F\u002F always handle unknown actions\n  }\n}\n```\n\nReturn the existing `state` unchanged in the `default` case — returning\n`undefined` causes subtle bugs. Always build and return a **new** object rather\nthan mutating `state`.\n",{"id":2313,"difficulty":121,"q":2314,"a":2315},"when-usereducer","When should you prefer useReducer over useState?","Prefer `useReducer` when: (1) state has **multiple sub-fields** that update\ntogether, (2) the **next state depends on complex logic** over the previous one,\nor (3) you want to **centralize update logic** so it's easy to test without\nrendering.\n\n```jsx\n\u002F\u002F useState: scattered setters, hard to audit\nsetLoading(true); setError(null); setData(null)\n\n\u002F\u002F useReducer: one dispatch, one place to read all transitions\ndispatch({ type: 'fetch_start' })\n```\n\nFor a simple counter or toggle, `useState` is cleaner. When you find yourself\ncalling multiple setters together or your update logic is branchy, `useReducer`\nmakes intent clearer and the logic testable in isolation.\n",{"id":2317,"difficulty":121,"q":2318,"a":2319},"init-arg","What is the third argument (initializer) of useReducer?","The optional third argument is an **initializer function**. React passes\n`initialArg` through it on the first render to compute the initial state — just\nlike lazy initialization in `useState`.\n\n```jsx\nfunction init(initialCount) {\n  return { count: initialCount }\n}\nconst [state, dispatch] = useReducer(reducer, 0, init)\n\u002F\u002F initial state: { count: 0 }\n```\n\nThe initializer is useful for computing expensive initial values, or for allowing\na \"reset to initial\" action — the reducer can call `init(action.payload)` to\nrebuild fresh initial state from a fresh argument.\n",{"id":2321,"difficulty":96,"q":2322,"a":2323},"action-type-convention","What are action type constants and why use them?","Action types are string identifiers for what happened. Defining them as named\nconstants prevents silent typos: a misspelled string dispatches to the `default`\ncase with no error, while a misspelled constant causes a `ReferenceError`.\n\n```jsx\n\u002F\u002F prone to typos\ndispatch({ type: 'incrment' }) \u002F\u002F silently hits default\n\n\u002F\u002F constants catch typos at the source\nconst INC = 'increment'\ndispatch({ type: INC })\n```\n\nIn larger codebases, co-locate the constants with the reducer or use TypeScript\nunion types for the action shape so the compiler enforces valid combinations.\n",{"id":2325,"difficulty":121,"q":2326,"a":2327},"sharing-dispatch","How do you share the dispatch function across components?","Combine `useReducer` with `useContext` — pass `dispatch` (and optionally `state`)\nthrough a context so any descendant can dispatch without prop drilling.\n\n```jsx\nconst StoreContext = createContext(null)\n\nfunction StoreProvider({ children }) {\n  const [state, dispatch] = useReducer(reducer, initial)\n  return (\n    \u003CStoreContext.Provider value={{ state, dispatch }}>\n      {children}\n    \u003C\u002FStoreContext.Provider>\n  )\n}\n\nfunction Counter() {\n  const { state, dispatch } = useContext(StoreContext)\n  return \u003Cbutton onClick={() => dispatch({ type: 'increment' })}>{state.count}\u003C\u002Fbutton>\n}\n```\n\n`dispatch` has stable identity across renders (React guarantees this), so putting\nit in context doesn't cause spurious re-renders.\n",{"id":2329,"difficulty":121,"q":2330,"a":2331},"reducer-reset","How do you implement a \"reset to initial\" action in a reducer?","Handle a dedicated `reset` action type that returns the initial state. If the\ninitial state was computed by an initializer, call it from within the reducer.\n\n```jsx\nconst initialState = { count: 0, text: '' }\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'increment': return { ...state, count: state.count + 1 }\n    case 'reset':     return initialState  \u002F\u002F back to start\n  }\n}\ndispatch({ type: 'reset' })\n```\n\nWhen initial state is dynamic (derived from props), pass it in the action payload\nand return it: `case 'reset': return action.payload`.\n",{"id":2333,"difficulty":121,"q":2334,"a":2335},"reducer-testing","How do you test a reducer function?","Because a reducer is a **pure function**, you test it directly — no React, no\nrendering, no mocking.\n\n```js\nimport { counterReducer } from '.\u002FcounterReducer'\n\ntest('increment adds 1', () => {\n  const next = counterReducer({ count: 5 }, { type: 'increment' })\n  expect(next.count).toBe(6)\n})\n\ntest('unknown action returns current state', () => {\n  const state = { count: 5 }\n  expect(counterReducer(state, { type: 'unknown' })).toBe(state)\n})\n```\n\nThis is one of `useReducer`'s key advantages: all your update logic is in a plain\nfunction you can cover with unit tests before wiring it up.\n",{"id":2337,"difficulty":121,"q":2338,"a":2339},"multiple-reducers","Can you use multiple useReducers in one component?","Yes. Each call is independent and manages a separate slice of state. Use multiple\nreducers when different parts of a component's state evolve via unrelated logic.\n\n```jsx\nconst [formState, formDispatch]   = useReducer(formReducer, initialForm)\nconst [uiState,   uiDispatch]     = useReducer(uiReducer,   initialUi)\n```\n\nSplitting avoids one monolithic reducer with unrelated cases. It's similar to\nsplitting `useState` calls for independent values.\n",{"id":2341,"difficulty":96,"q":2342,"a":2343},"action-payload","How do you pass data with a dispatch action?","Add any extra data as properties on the action object — typically called\n`payload` by convention (borrowed from Flux\u002FRedux).\n\n```jsx\ndispatch({ type: 'set_user', payload: { id: 1, name: 'Ada' } })\n\n\u002F\u002F reducer\ncase 'set_user': return { ...state, user: action.payload }\n```\n\nThe payload shape is up to you — some teams prefer `{ type, id, name }` instead\nof nesting under `payload`. Consistency matters more than the exact shape; pick\none convention and stick to it.\n",{"id":2345,"difficulty":208,"q":2346,"a":2347},"immer-reducer","How does Immer simplify reducer logic?","**Immer**'s `produce` wraps a reducer so you can *write* mutating code on a\ndraft, and it produces a correctly immutable new state behind the scenes.\n\n```jsx\nimport produce from 'immer'\n\nconst reducer = produce((draft, action) => {\n  switch (action.type) {\n    case 'add_item':\n      draft.items.push(action.item) \u002F\u002F looks like mutation, but it's safe\n      break\n    case 'update_city':\n      draft.user.address.city = action.city \u002F\u002F deep update without spreading\n      break\n  }\n})\n```\n\nThis eliminates the spreading pyramid for deeply nested state without sacrificing\nimmutability. Redux Toolkit uses Immer internally for the same reason.\n",{"id":2349,"difficulty":121,"q":2350,"a":2351},"reducer-vs-redux","How does useReducer compare to Redux?","`useReducer` gives you the same **reducer pattern** locally, but lacks Redux's\necosystem: global store singleton, devtools time-travel, middleware for async\nlogic, and selector memoization.\n\n| | useReducer | Redux Toolkit |\n|---|---|---|\n| Scope | component \u002F context tree | entire app |\n| DevTools | none | yes |\n| Middleware | none | thunk, saga, etc. |\n| Async | manually via effects | built-in |\n\nFor local or module-level state, `useReducer` + context is often enough. For\ncross-cutting, high-churn global state, Redux Toolkit or Zustand adds more\ninfrastructure with less boilerplate.\n",{"id":2353,"difficulty":121,"q":2354,"a":2355},"avoid-mutation","What happens if you mutate state inside a reducer?","Mutating the state object and returning it gives React the **same reference**, so\nit bails out and skips the re-render — your UI appears frozen even though the\ndata changed.\n\n```jsx\n\u002F\u002F broken: mutates the original, same reference returned\ncase 'push':\n  state.items.push(action.item) \u002F\u002F mutates!\n  return state                  \u002F\u002F same ref -> no re-render\n\n\u002F\u002F correct: new array, new object\ncase 'push':\n  return { ...state, items: [...state.items, action.item] }\n```\n\nThis is the same rule as `useState`: always return a **new** object\u002Farray for\nchanged values. Immer enforces this for you automatically.\n",{"id":2357,"difficulty":208,"q":2358,"a":2359},"dispatch-batching","Are multiple dispatches in one event handler batched?","Yes. In React 18, all state updates — including multiple `dispatch` calls — inside\nevent handlers and async callbacks are **automatically batched** into a single\nre-render.\n\n```jsx\nfunction handleReset() {\n  dispatch({ type: 'reset_form' })\n  dispatch({ type: 'clear_errors' })\n  \u002F\u002F ONE re-render in React 18, not two\n}\n```\n\nIn React 17, only updates inside React event handlers were batched; async\ncallbacks caused separate renders per dispatch. If you need to force synchronous\nintermediate renders (rarely), use `flushSync` from `react-dom`.\n",{"id":2361,"difficulty":121,"q":2362,"a":2363},"usereducer-no-side-effects","Why must a reducer have no side effects?","React may call the reducer **more than once** in Strict Mode (development), and\nconcurrent features can discard and replay state updates. A reducer with side\neffects (API calls, timers, random numbers, mutations) would break or run the\nside effect multiple times unpredictably.\n\n```jsx\n\u002F\u002F broken: side effect in reducer\ncase 'save':\n  fetch('\u002Fapi\u002Fsave', { body: JSON.stringify(state) }) \u002F\u002F runs on every replay\n  return state\n\n\u002F\u002F correct: reducer only computes state\ncase 'save': return { ...state, saving: true }\n\u002F\u002F then fire the request in a useEffect that watches `saving`\n```\n\nKeep reducers **pure** — dispatch + effect is the correct pattern for actions\nthat need async work alongside them.\n",{"id":2365,"difficulty":121,"q":2366,"a":2367},"lazy-usereducer-init","When would you use the initializer function for performance?","When the initial state requires an **expensive computation** (parsing a large JSON\nblob, reading from localStorage, complex filtering), pass it as the third\nargument so it only runs once — not on every render like a value expression would.\n\n```jsx\nfunction buildInitialState(data) {\n  return { items: parse(data), selected: null } \u002F\u002F expensive parse\n}\n\nconst [state, dispatch] = useReducer(reducer, rawData, buildInitialState)\n\u002F\u002F buildInitialState(rawData) runs once on mount\n```\n\nWithout the initializer, `buildInitialState(rawData)` in the second argument\nwould still only be used once (the initialState arg is also only read once), so\nthe main win is clarity and the ability to reuse the function for reset actions.\n",{"description":94},"React useReducer interview questions — reducer functions, dispatch, action types, initializers, context integration, and when to prefer useReducer over useState.","react\u002Fhooks\u002Fusereducer","useReducer","QhWz1FylI4XYks-BksLWHTkzUOvcMTj3d_IVtz8Ntp8",{"id":2374,"title":2375,"body":2376,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":2380,"navigation":100,"order":46,"path":2381,"questions":2382,"questionsCount":589,"related":169,"seo":2463,"seoDescription":2464,"stem":2465,"subtopic":2466,"topic":72,"topicSlug":74,"updated":420,"__hash__":2467},"qa\u002Freact\u002Fpatterns\u002Fportals-refs.md","Portals Refs",{"type":91,"value":2377,"toc":2378},[],{"title":94,"searchDepth":11,"depth":11,"links":2379},[],{},"\u002Freact\u002Fpatterns\u002Fportals-refs",[2383,2387,2391,2395,2399,2403,2407,2411,2415,2419,2423,2427,2431,2435,2439,2443,2447,2451,2455,2459],{"id":2384,"difficulty":96,"q":2385,"a":2386},"what-is-createportal","What does ReactDOM.createPortal do and when should you use it?","`ReactDOM.createPortal(children, domNode)` renders `children` into a **different\nDOM node** than the component's parent, while keeping them inside the **React\ncomponent tree**. The portal children still receive context, state, and events\nfrom their React parent — only the DOM placement changes.\n\n```jsx\nimport { createPortal } from 'react-dom'\n\nfunction Modal({ children, onClose }) {\n  return createPortal(\n    \u002F\u002F JSX rendered here...\n    \u003Cdiv className=\"modal-overlay\" onClick={onClose}>\n      \u003Cdiv className=\"modal-content\" onClick={e => e.stopPropagation()}>\n        {children}\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>,\n    \u002F\u002F ...but mounted into this DOM node, outside the app root\n    document.getElementById('modal-root')\n  )\n}\n```\n\nUse portals when you need a component to **escape the CSS stacking context** of\nits parent — modals, tooltips, dropdown menus, toasts, and popovers all commonly\nneed to render at the `\u003Cbody>` level to avoid `overflow: hidden` or `z-index`\nclipping from ancestor elements.\n\n**Rule of thumb:** Reach for `createPortal` any time a UI element needs to visually\nfloat above the rest of the page, regardless of where it lives in the component tree.\n",{"id":2388,"difficulty":121,"q":2389,"a":2390},"portal-react-tree-vs-dom-tree","A portal renders its children in a different DOM node. Does that affect React context or event propagation?","No — portals are a **DOM-only escape hatch**. React's virtual component tree is\nunchanged, so everything that flows through the React tree — **context values,\nstate, refs, and synthetic events** — still works as if the portal were rendered\ninline.\n\n```jsx\nfunction App() {\n  const [theme] = useContext(ThemeContext) \u002F\u002F 'dark'\n\n  return (\n    \u003CThemeContext.Provider value=\"dark\">\n      \u003CModal>\n        {\u002F* ThemeContext reads 'dark' even though Modal\n            is mounted under document.body in the DOM *\u002F}\n        \u003CThemedButton \u002F>\n      \u003C\u002FModal>\n    \u003C\u002FThemeContext.Provider>\n  )\n}\n```\n\nThis means a portal component can consume any context provided by its React\nancestors, even if those ancestors are DOM siblings or even DOM ancestors of the\nportal's mount point.\n\n**Rule of thumb:** Think of portals as a teleport for DOM placement only — the\nReact component lineage is unaffected, so all React features keep working normally.\n",{"id":2392,"difficulty":121,"q":2393,"a":2394},"portal-event-bubbling-gotcha","How do synthetic events bubble through a portal? Why is this a common interview gotcha?","Synthetic events inside a portal bubble **through the React component tree, not\nthe DOM tree**. A click inside a modal portal bubbles up to the React ancestor\nthat rendered the portal, even though the modal's DOM node is a sibling of\n`#root` in the actual DOM.\n\n```jsx\nfunction Page() {\n  \u002F\u002F This handler fires when the button inside Modal is clicked,\n  \u002F\u002F because the portal's React parent is Page — even though\n  \u002F\u002F the modal DOM is outside #root entirely.\n  const handleClick = () => console.log('caught in Page')\n\n  return (\n    \u003Cdiv onClick={handleClick}>\n      \u003CModal>\n        \u003Cbutton>Click me\u003C\u002Fbutton> {\u002F* bubbles to Page via React tree *\u002F}\n      \u003C\u002FModal>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\nThe gotcha: if you add a `document.addEventListener('click', ...)` outside React\nand expect portal clicks to bubble through the DOM, you'll see them — but a React\n`onClick` placed on a DOM ancestor of `#root` will also fire because the native\nevent does travel through the real DOM too. The **React synthetic event** system\nis what bubbles through the React tree.\n\n**Rule of thumb:** Always think of event bubbling in terms of the React component\ntree, not the rendered DOM tree — portals don't change the React ancestry.\n",{"id":2396,"difficulty":96,"q":2397,"a":2398},"portal-modal-z-index-escape","Why do modals rendered inside a deeply nested component often break z-index stacking, and how does createPortal fix it?","CSS `z-index` is scoped to a **stacking context**. An ancestor with\n`transform`, `opacity \u003C 1`, `position: relative\u002Fabsolute` and a `z-index`, or\n`overflow: hidden` creates a new stacking context that caps the `z-index` of all\nits descendants. A modal with `z-index: 9999` is still trapped below an ancestor\nwith `z-index: 1` if that ancestor forms its own stacking context.\n\n```jsx\n\u002F\u002F Without portal — modal trapped inside stacking context of .card\n\u002F\u002F \u003Cdiv class=\"card\" style=\"transform: translateZ(0); z-index: 1\">\n\u002F\u002F   \u003CModal \u002F>   ← z-index: 9999 but still below sibling stacking contexts\n\u002F\u002F \u003C\u002Fdiv>\n\n\u002F\u002F With portal — modal escapes to body, outside all stacking contexts\nfunction Modal({ children }) {\n  return createPortal(\n    \u003Cdiv className=\"modal\" style={{ zIndex: 9999 }}>\n      {children}\n    \u003C\u002Fdiv>,\n    document.body \u002F\u002F top-level stacking context\n  )\n}\n```\n\nPortaling to `document.body` (or a dedicated `#modal-root` sibling of `#root`)\nremoves the modal from all ancestor stacking contexts and lets it freely float\nabove every other element.\n\n**Rule of thumb:** If a modal or tooltip is clipped or covered despite a high\n`z-index`, the component tree has a stacking context trap — use `createPortal`\nto escape it.\n",{"id":2400,"difficulty":121,"q":2401,"a":2402},"portal-accessibility-focus","How should you handle focus management and accessibility for a portal-based modal?","A modal portal must manage **focus trap**, **aria attributes**, and **keyboard\ndismissal** explicitly, because rendering outside the normal DOM flow doesn't\nautomatically handle any of these.\n\n```jsx\nimport { useEffect, useRef } from 'react'\nimport { createPortal } from 'react-dom'\n\nfunction Modal({ isOpen, onClose, children }) {\n  const dialogRef = useRef(null)\n\n  useEffect(() => {\n    if (!isOpen) return\n    \u002F\u002F Move focus into the modal when it opens\n    dialogRef.current?.focus()\n    \u002F\u002F Close on Escape\n    const onKey = (e) => e.key === 'Escape' && onClose()\n    document.addEventListener('keydown', onKey)\n    return () => document.removeEventListener('keydown', onKey)\n  }, [isOpen, onClose])\n\n  if (!isOpen) return null\n\n  return createPortal(\n    \u003Cdiv\n      role=\"dialog\"\n      aria-modal=\"true\"\n      tabIndex={-1}  \u002F\u002F makes div focusable\n      ref={dialogRef}\n      className=\"modal\"\n    >\n      {children}\n    \u003C\u002Fdiv>,\n    document.body\n  )\n}\n```\n\nKey requirements: `role=\"dialog\"`, `aria-modal=\"true\"` (tells screen readers\nto ignore background content), focus moved into the dialog on open, and Escape\nkey closes it. Libraries like `focus-trap-react` automate the focus-trap cycle.\n\n**Rule of thumb:** A portal gives you DOM freedom, but accessibility is still\nyour responsibility — always add `role=\"dialog\"`, `aria-modal`, and focus management.\n",{"id":2404,"difficulty":121,"q":2405,"a":2406},"portal-cleanup","How does React clean up a portal when the component unmounts?","React automatically removes portal content from the DOM when the component\nthat rendered the portal unmounts. You do not need to manually call\n`ReactDOM.unmountComponentAtNode` for standard portals.\n\n```jsx\nfunction App() {\n  const [showModal, setShowModal] = useState(false)\n\n  return (\n    \u003C>\n      \u003Cbutton onClick={() => setShowModal(true)}>Open\u003C\u002Fbutton>\n      {\u002F* When showModal becomes false, React unmounts Modal and\n          removes its portal DOM from document.body automatically *\u002F}\n      {showModal && (\n        \u003CModal onClose={() => setShowModal(false)}>\n          \u003Cp>Content\u003C\u002Fp>\n        \u003C\u002FModal>\n      )}\n    \u003C\u002F>\n  )\n}\n```\n\nIf you **dynamically created the mount node** yourself (e.g. with\n`document.createElement('div')` and `appendChild`), you are responsible for\nremoving that container element in a cleanup function or `useEffect` return.\nThe portal content inside it is still cleaned by React; the container node\nitself is your concern.\n\n**Rule of thumb:** Let React manage unmounting; only clean up manually if you\ncreated the portal's host DOM element dynamically outside of React.\n",{"id":2408,"difficulty":208,"q":2409,"a":2410},"portal-testing","How do you test a component that uses createPortal?","Jest + React Testing Library (RTL) works with portals out of the box because\nRTL renders into `document.body` by default and `createPortal` also targets\n`document.body` — so portal content is in `document.body` and queryable normally.\n\n```jsx\n\u002F\u002F Modal.test.jsx\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport Modal from '.\u002FModal'\n\ntest('renders portal content in document.body', () => {\n  render(\u003CModal isOpen onClose={() => {}}>Hello portal\u003C\u002FModal>)\n  \u002F\u002F Even though content is in a portal, RTL queries search document.body\n  expect(screen.getByText('Hello portal')).toBeInTheDocument()\n})\n\ntest('calls onClose on Escape key', async () => {\n  const onClose = jest.fn()\n  render(\u003CModal isOpen onClose={onClose}>Content\u003C\u002FModal>)\n  await userEvent.keyboard('{Escape}')\n  expect(onClose).toHaveBeenCalled()\n})\n```\n\nIf your portal targets a custom node (e.g. `document.getElementById('modal-root')`),\nadd `\u003Cdiv id=\"modal-root\" \u002F>` to `document.body` in a `beforeEach` and clean it\nup in `afterEach`.\n\n**Rule of thumb:** RTL's document-level query approach means portals require no\nspecial setup — only custom mount targets need a manual DOM fixture.\n",{"id":2412,"difficulty":96,"q":2413,"a":2414},"what-is-useref","What does useRef return and what are its two main use cases?","`useRef(initialValue)` returns a **plain mutable object** `{ current: initialValue }`\nthat persists for the entire lifetime of the component. Mutating `.current` does\n**not** trigger a re-render.\n\n```jsx\n\u002F\u002F Use case 1: DOM access\nfunction TextInput() {\n  const inputRef = useRef(null)\n  const focus = () => inputRef.current.focus() \u002F\u002F imperative DOM call\n  return \u003Cinput ref={inputRef} \u002F>\n}\n\n\u002F\u002F Use case 2: mutable container (timer id, previous value, etc.)\nfunction Interval() {\n  const timerId = useRef(null)\n  const start = () => { timerId.current = setInterval(tick, 1000) }\n  const stop = () => clearInterval(timerId.current)\n  return \u003C>\u003Cbutton onClick={start}>Start\u003C\u002Fbutton>\u003Cbutton onClick={stop}>Stop\u003C\u002Fbutton>\u003C\u002F>\n}\n```\n\nThe two distinct jobs: (1) **hold a reference to a DOM element** so you can call\nimperative DOM APIs (focus, scroll, measure); (2) **store any mutable value** that\nmust survive re-renders without causing them — timers, intervals, previous state,\nanimation frame IDs, WebSocket instances.\n\n**Rule of thumb:** If you need to read or write a value across renders without\ntriggering one, `useRef` is the right tool — it's essentially instance state for\nfunction components.\n",{"id":2416,"difficulty":121,"q":2417,"a":2418},"ref-vs-state","What is the key difference between a ref and state, and when would you choose one over the other?","The fundamental difference: **state changes schedule a re-render; ref mutations\ndo not**. Both persist across renders, but only state drives the UI.\n\n```jsx\nfunction Counter() {\n  const [count, setCount] = useState(0) \u002F\u002F changing → re-render\n  const renderCount = useRef(0)         \u002F\u002F changing → no re-render\n\n  useEffect(() => {\n    renderCount.current += 1 \u002F\u002F track silently, no render loop\n  })\n\n  return (\n    \u003Cdiv>\n      \u003Cp>Count: {count}\u003C\u002Fp>                       {\u002F* must be state *\u002F}\n      \u003Cp>Renders: {renderCount.current}\u003C\u002Fp>       {\u002F* stale between renders, but OK here *\u002F}\n      \u003Cbutton onClick={() => setCount(c => c + 1)}>+1\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\nRef mutations are also **synchronous** — `ref.current` updates immediately with\nno batching. State updates may be batched and applied asynchronously.\n\nChoose **state** when the value needs to appear in the rendered output or drive\nconditional logic inside JSX. Choose **ref** when the value is used by event\nhandlers \u002F effects only and shouldn't re-render the component when it changes.\n\n**Rule of thumb:** If a value needs to be visible in the UI, it must be state;\nif it's behind-the-scenes bookkeeping, a ref keeps the render cycle clean.\n",{"id":2420,"difficulty":121,"q":2421,"a":2422},"callback-ref","What is a callback ref and when is it more useful than useRef?","A **callback ref** is a function passed to the `ref` prop instead of a ref\nobject. React calls it with the DOM node when the component mounts, and with\n`null` when it unmounts.\n\n```jsx\nfunction MeasuredBox() {\n  const [height, setHeight] = useState(0)\n\n  \u002F\u002F Called once with the DOM node on mount, null on unmount\n  const measuredRef = useCallback((node) => {\n    if (node !== null) {\n      setHeight(node.getBoundingClientRect().height)\n    }\n  }, []) \u002F\u002F stable reference — no re-registration on every render\n\n  return (\n    \u003Cdiv ref={measuredRef}>\n      Height is {height}px\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n`useRef` gives you the node after render but doesn't notify you when the node\nchanges. A callback ref is superior when you need to **react to the node being\nattached or detached** — measuring layout after conditional rendering, attaching\nthird-party libraries imperatively, or observing dynamic child nodes.\n\n**Rule of thumb:** Use `useRef` for stable, always-mounted nodes; use a callback\nref when you need to run code the moment the element appears or disappears in the DOM.\n",{"id":2424,"difficulty":121,"q":2425,"a":2426},"ref-forwarding","What is ref forwarding and why is it needed for custom components?","By default, the `ref` prop on a custom component is **not forwarded** to the\nunderlying DOM element — React reserves `ref` and doesn't pass it through `props`.\n`React.forwardRef` opts a component into forwarding the ref to a child element.\n\n```jsx\n\u002F\u002F Without forwardRef: ref on \u003CFancyInput> points to nothing useful\n\u002F\u002F With forwardRef: ref reaches the real \u003Cinput>\nconst FancyInput = React.forwardRef((props, ref) => (\n  \u003Cinput\n    {...props}\n    ref={ref}              \u002F\u002F forwarded ref attached to DOM input\n    className=\"fancy\"\n  \u002F>\n))\n\n\u002F\u002F Parent can now focus the input imperatively\nfunction Form() {\n  const inputRef = useRef(null)\n  return (\n    \u003C>\n      \u003CFancyInput ref={inputRef} placeholder=\"Type here\" \u002F>\n      \u003Cbutton onClick={() => inputRef.current.focus()}>Focus\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\nRef forwarding is common in **design-system \u002F component-library** components\n(buttons, inputs, modals) that wrap a native element but need to expose the DOM\nnode to consumers for accessibility or focus management.\n\n**Rule of thumb:** Any reusable component that wraps a DOM element should use\n`forwardRef` so library consumers retain imperative DOM access.\n",{"id":2428,"difficulty":208,"q":2429,"a":2430},"useimperativehandle","What does useImperativeHandle do, and when should you use it?","`useImperativeHandle(ref, createHandle, deps)` lets a child component **control\nwhat the parent's ref exposes** — instead of exposing the raw DOM node, it\nexposes a custom object with only the methods the parent should call.\n\n```jsx\nconst VideoPlayer = React.forwardRef((props, ref) => {\n  const videoRef = useRef(null)\n\n  \u002F\u002F Parent only gets play\u002Fpause — not direct DOM access\n  useImperativeHandle(ref, () => ({\n    play: () => videoRef.current.play(),\n    pause: () => videoRef.current.pause(),\n  }), [])\n\n  return \u003Cvideo ref={videoRef} src={props.src} \u002F>\n})\n\nfunction App() {\n  const playerRef = useRef(null)\n  return (\n    \u003C>\n      \u003CVideoPlayer ref={playerRef} src=\"\u002Fclip.mp4\" \u002F>\n      \u003Cbutton onClick={() => playerRef.current.play()}>Play\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\nThis is an **encapsulation tool**: it prevents parent components from reaching\ninto child internals and calling arbitrary DOM methods, keeping the API surface\nintentionally minimal.\n\n**Rule of thumb:** Prefer `useImperativeHandle` over raw `forwardRef` whenever\nyou need to expose imperative behavior from a child component — it enforces a\nclean, intentional API boundary.\n",{"id":2432,"difficulty":121,"q":2433,"a":2434},"ref-mutable-container-previous-value","How do you use a ref to track the previous value of a prop or state variable?","Refs persist across renders without resetting, so they're perfect for storing\nthe **value from the last render** before state\u002Fprops update. The pattern is a\none-liner `useEffect` that writes `.current` after every render.\n\n```jsx\nfunction usePrevious(value) {\n  const ref = useRef(undefined)\n\n  useEffect(() => {\n    \u002F\u002F Runs after render — so during the current render, ref.current\n    \u002F\u002F still holds the value from the PREVIOUS render\n    ref.current = value\n  })\n\n  return ref.current \u002F\u002F previous render's value\n}\n\nfunction PriceDisplay({ price }) {\n  const prevPrice = usePrevious(price)\n  const direction = price > prevPrice ? '▲' : '▼'\n  return \u003Cspan>{price} {prevPrice !== undefined && direction}\u003C\u002Fspan>\n}\n```\n\nThis works because `useEffect` runs after the render is committed to the DOM.\nDuring the render that reads `ref.current`, the effect from the previous render\nhas already written the old value in, but the new value hasn't been written yet.\n\n**Rule of thumb:** Store previous values in a ref, not state — reading the\nprevious value shouldn't cause an extra re-render.\n",{"id":2436,"difficulty":121,"q":2437,"a":2438},"ref-timeout-cleanup","Why should you store setTimeout or setInterval IDs in a ref rather than state?","Storing a timer ID in **state** would trigger a re-render every time you start\nor clear a timer, which is wasteful and can cause cascading effects. A **ref**\nstores it silently.\n\n```jsx\nfunction Debounced({ onSearch }) {\n  const timerRef = useRef(null)\n\n  const handleChange = (e) => {\n    \u002F\u002F Cancel previous timer without re-rendering\n    clearTimeout(timerRef.current)\n    timerRef.current = setTimeout(() => {\n      onSearch(e.target.value)\n    }, 300)\n  }\n\n  \u002F\u002F Clean up on unmount to prevent calling onSearch after component is gone\n  useEffect(() => {\n    return () => clearTimeout(timerRef.current)\n  }, [])\n\n  return \u003Cinput onChange={handleChange} \u002F>\n}\n```\n\nThe same pattern applies to `requestAnimationFrame` IDs, `setInterval` IDs,\nWebSocket instances, and Intersection\u002FResizeObserver instances — anything that\nneeds to be cancelled\u002Fdestroyed on unmount but doesn't affect the render output.\n\n**Rule of thumb:** Anything you'd cancel in cleanup belongs in a ref, not state —\ntimer IDs and resource handles are bookkeeping, not UI data.\n",{"id":2440,"difficulty":121,"q":2441,"a":2442},"accessing-child-dom-from-parent","How do you access a child component's DOM node from a parent component?","The standard approach is `React.forwardRef` in the child, which allows the\nparent to pass a ref that the child attaches to its DOM element.\n\n```jsx\n\u002F\u002F Child forwards ref to its inner \u003Cdiv>\nconst Card = React.forwardRef(({ title, children }, ref) => (\n  \u003Cdiv ref={ref} className=\"card\">\n    \u003Ch2>{title}\u003C\u002Fh2>\n    {children}\n  \u003C\u002Fdiv>\n))\n\n\u002F\u002F Parent attaches ref and can read the DOM node\nfunction Page() {\n  const cardRef = useRef(null)\n\n  const scrollIntoView = () => {\n    cardRef.current?.scrollIntoView({ behavior: 'smooth' })\n  }\n\n  return (\n    \u003C>\n      \u003Cbutton onClick={scrollIntoView}>Scroll to card\u003C\u002Fbutton>\n      \u003CCard ref={cardRef} title=\"Target\">content\u003C\u002FCard>\n    \u003C\u002F>\n  )\n}\n```\n\nFor class components, a plain `ref` already points to the class instance, so\nyou can call public methods directly without `forwardRef`. For function\ncomponents, `forwardRef` + optionally `useImperativeHandle` is the only way.\n\n**Rule of thumb:** Always use `forwardRef` in any reusable function component\nthat wraps a DOM element — parents often need scroll, focus, or measurement access.\n",{"id":2444,"difficulty":208,"q":2445,"a":2446},"refs-as-design-smell","When do refs indicate a design smell in a React component?","Refs are an **imperative escape hatch** — reaching for them frequently is a sign\nthe component is fighting React's declarative model instead of working with it.\n\n```jsx\n\u002F\u002F Smell: using ref to read input value instead of controlled state\nfunction BadForm() {\n  const inputRef = useRef(null)\n  const submit = () => console.log(inputRef.current.value) \u002F\u002F side-channel read\n  return \u003C>\u003Cinput ref={inputRef} \u002F>\u003Cbutton onClick={submit}>Send\u003C\u002Fbutton>\u003C\u002F>\n}\n\n\u002F\u002F Better: controlled component — value lives in React state\nfunction GoodForm() {\n  const [value, setValue] = useState('')\n  const submit = () => console.log(value) \u002F\u002F just reads state\n  return (\n    \u003C>\n      \u003Cinput value={value} onChange={e => setValue(e.target.value)} \u002F>\n      \u003Cbutton onClick={submit}>Send\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\nOther ref-as-smell patterns: using a ref to trigger a re-render manually (use\nstate), using a ref to share data between sibling components (lift state or use\ncontext), and using refs to replicate what `useEffect` dependencies should handle.\n\n**Rule of thumb:** If you're reaching for a ref to solve a problem involving\nrendering or data flow, step back — there's almost certainly a declarative solution\nusing state, context, or derived values.\n",{"id":2448,"difficulty":208,"q":2449,"a":2450},"ref-render-cycle","Why can't you reliably read a ref's value during the render phase?","Refs are **mutable and not part of React's render model**. React may render a\ncomponent multiple times before committing (strict mode renders twice in dev;\nconcurrent mode may discard renders). Refs are only reliably up to date **after\ncommit** (i.e. inside effects and event handlers).\n\n```jsx\nfunction Broken() {\n  const ref = useRef(0)\n  ref.current++ \u002F\u002F mutated during render — unreliable in concurrent mode\n\n  \u002F\u002F If React renders this component but discards the output,\n  \u002F\u002F ref.current has been incremented but nothing was committed.\n  return \u003Cp>Renders: {ref.current}\u003C\u002Fp> \u002F\u002F stale \u002F unpredictable\n}\n\n\u002F\u002F Correct: mutate refs only in effects or event handlers\nfunction Fixed() {\n  const ref = useRef(0)\n  useEffect(() => { ref.current++ }) \u002F\u002F runs only after commit\n  return \u003Cp>...\u003C\u002Fp>\n}\n```\n\nReading a ref in JSX (displaying `ref.current`) is also fragile because React\nwon't schedule a re-render when `.current` changes, so the displayed value goes\nstale silently.\n\n**Rule of thumb:** Treat refs as post-commit bookkeeping — read and write `.current`\nonly inside effects and event handlers, never directly in the render function body.\n",{"id":2452,"difficulty":96,"q":2453,"a":2454},"portal-vs-conditional-render","When should you use a portal versus simply conditionally rendering a component at the top of the tree?","Both approaches can place a modal high in the DOM, but they have different\ntradeoffs for **component co-location** and **CSS context**.\n\n```jsx\n\u002F\u002F Option A: lift modal to App — modal logic scattered from its trigger\nfunction App() {\n  const [open, setOpen] = useState(false)\n  return (\n    \u003C>\n      {open && \u003CModal onClose={() => setOpen(false)} \u002F>} {\u002F* far from trigger *\u002F}\n      \u003CDeepTree onTrigger={() => setOpen(true)} \u002F>\n    \u003C\u002F>\n  )\n}\n\n\u002F\u002F Option B: portal — modal stays co-located with its trigger, escapes DOM\nfunction TriggerButton() {\n  const [open, setOpen] = useState(false)\n  return (\n    \u003C>\n      \u003Cbutton onClick={() => setOpen(true)}>Open Modal\u003C\u002Fbutton>\n      {open && \u003CModal onClose={() => setOpen(false)} \u002F>} {\u002F* portal inside *\u002F}\n    \u003C\u002F>\n  )\n}\n```\n\nPortals let you keep state and logic **co-located** with the component that owns\nthem while still rendering the output at the top of the DOM. Lifting state\nachieves DOM placement but couples the modal logic to a distant ancestor.\n\n**Rule of thumb:** Use a portal when co-location matters and a CSS escape is\nneeded; lift state to the root only when multiple unrelated components need to\ncontrol the same modal.\n",{"id":2456,"difficulty":121,"q":2457,"a":2458},"ref-scroll-measure","How do you use a ref to scroll to an element or measure its dimensions?","Attaching a ref to a DOM element gives you the raw `HTMLElement`, so you can\ncall any imperative DOM API — `scrollIntoView`, `getBoundingClientRect`,\n`offsetHeight`, etc. Measurements must happen **after the element has rendered**,\nwhich means inside a `useEffect` or an event handler.\n\n```jsx\nfunction ChatWindow({ messages }) {\n  const bottomRef = useRef(null)\n  const containerRef = useRef(null)\n  const [containerHeight, setContainerHeight] = useState(0)\n\n  \u002F\u002F Scroll to bottom whenever messages change\n  useEffect(() => {\n    bottomRef.current?.scrollIntoView({ behavior: 'smooth' })\n  }, [messages])\n\n  \u002F\u002F Measure container after first render\n  useEffect(() => {\n    if (containerRef.current) {\n      setContainerHeight(containerRef.current.getBoundingClientRect().height)\n    }\n  }, [])\n\n  return (\n    \u003Cdiv ref={containerRef} className=\"chat-window\">\n      {messages.map(m => \u003CMessage key={m.id} data={m} \u002F>)}\n      \u003Cdiv ref={bottomRef} \u002F> {\u002F* sentinel element at the bottom *\u002F}\n    \u003C\u002Fdiv>\n  )\n}\n```\n\nFor **layout measurements that need to happen before the browser paints**, use\n`useLayoutEffect` instead of `useEffect` — it fires synchronously after DOM\nmutations but before the browser renders, avoiding a visible flash.\n\n**Rule of thumb:** Use `useEffect` for scroll commands (visual, after paint is\nfine) and `useLayoutEffect` for measurements that affect what gets rendered,\nsince those must happen before the user sees the result.\n",{"id":2460,"difficulty":121,"q":2461,"a":2462},"multiple-portals","Can you render multiple portals on the same page, and how should you manage their mount points?","Yes — you can have as many portals as you like. Each `createPortal` call is\nindependent and can target the **same or different** DOM nodes.\n\n```jsx\n\u002F\u002F index.html — dedicated mount points alongside #root\n\u002F\u002F \u003Cdiv id=\"root\">\u003C\u002Fdiv>\n\u002F\u002F \u003Cdiv id=\"modal-root\">\u003C\u002Fdiv>\n\u002F\u002F \u003Cdiv id=\"tooltip-root\">\u003C\u002Fdiv>\n\nfunction Tooltip({ content, anchor }) {\n  return createPortal(\n    \u003Cdiv className=\"tooltip\">{content}\u003C\u002Fdiv>,\n    document.getElementById('tooltip-root') \u002F\u002F dedicated node\n  )\n}\n\nfunction Modal({ children }) {\n  return createPortal(\n    \u003Cdiv className=\"modal-overlay\">{children}\u003C\u002Fdiv>,\n    document.getElementById('modal-root') \u002F\u002F separate node for stacking control\n  )\n}\n```\n\nSeparating mount nodes gives you **CSS stacking control** between portal types —\n`#modal-root` can have a higher `z-index` base than `#tooltip-root` at the body\nlevel, without needing per-element `z-index` wars.\n\n**Rule of thumb:** Provision a dedicated `\u003Cdiv>` for each class of portal\n(modals, tooltips, toasts) in `index.html` so their stacking order is\ndetermined structurally, not by arbitrary `z-index` values.\n",{"description":94},"React portals and refs interview questions — createPortal, DOM escape hatches, useRef, callback refs, ref forwarding, and direct DOM manipulation patterns.","react\u002Fpatterns\u002Fportals-refs","Portals & Refs","NfTRyqGUI9gF4wkwTyXVqCdmHs08NsxKVml1iJSZVjc",{"id":2469,"title":2470,"body":2471,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":2475,"navigation":100,"order":46,"path":2477,"questions":2478,"questionsCount":493,"related":169,"seo":2539,"seoDescription":2540,"stem":2541,"subtopic":2542,"topic":45,"topicSlug":47,"updated":420,"__hash__":2543},"qa\u002Freact\u002Frendering-and-performance\u002Fcode-splitting-lazy.md","Code Splitting Lazy",{"type":91,"value":2472,"toc":2473},[],{"title":94,"searchDepth":11,"depth":11,"links":2474},[],{"subtopicSlug":2476},"code-splitting-lazy","\u002Freact\u002Frendering-and-performance\u002Fcode-splitting-lazy",[2479,2483,2487,2491,2495,2499,2503,2507,2511,2515,2519,2523,2527,2531,2535],{"id":2480,"difficulty":96,"q":2481,"a":2482},"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":2484,"difficulty":96,"q":2485,"a":2486},"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":2488,"difficulty":96,"q":2489,"a":2490},"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":2492,"difficulty":121,"q":2493,"a":2494},"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":2496,"difficulty":96,"q":2497,"a":2498},"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":2500,"difficulty":121,"q":2501,"a":2502},"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":2504,"difficulty":121,"q":2505,"a":2506},"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":2508,"difficulty":121,"q":2509,"a":2510},"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":2512,"difficulty":121,"q":2513,"a":2514},"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":2516,"difficulty":96,"q":2517,"a":2518},"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":2520,"difficulty":208,"q":2521,"a":2522},"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":2524,"difficulty":121,"q":2525,"a":2526},"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":2528,"difficulty":121,"q":2529,"a":2530},"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":2532,"difficulty":208,"q":2533,"a":2534},"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":2536,"difficulty":121,"q":2537,"a":2538},"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":94},"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":2545,"title":2546,"body":2547,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":2551,"navigation":100,"order":46,"path":2552,"questions":2553,"questionsCount":589,"related":169,"seo":2633,"seoDescription":2634,"stem":2635,"subtopic":2546,"topic":54,"topicSlug":56,"updated":420,"__hash__":2636},"qa\u002Freact\u002Frouting\u002Fprotected-routes.md","Protected Routes",{"type":91,"value":2548,"toc":2549},[],{"title":94,"searchDepth":11,"depth":11,"links":2550},[],{},"\u002Freact\u002Frouting\u002Fprotected-routes",[2554,2558,2562,2566,2570,2574,2578,2582,2586,2590,2594,2598,2602,2606,2609,2613,2617,2621,2625,2629],{"id":2555,"difficulty":96,"q":2556,"a":2557},"what-is-protected-route","What is a protected route and why is it needed in a React SPA?","A **protected route** is a route that only renders its component when the user meets\na certain condition — typically being authenticated. Without it, any visitor who knows\na URL can directly navigate to sensitive pages (dashboards, admin panels, account\nsettings) because the browser fetches the static JS bundle and renders the page\nentirely client-side.\n\nThe solution is an **auth guard** component that checks the auth state before\nrendering child routes. If the check fails it **redirects** to the login page instead\nof rendering the protected content.\n\n```jsx\n\u002F\u002F Minimal guard — renders children or redirects\nfunction RequireAuth({ children }) {\n  const { user } = useAuth(); \u002F\u002F check auth state\n  if (!user) {\n    return \u003CNavigate to=\"\u002Flogin\" replace \u002F>; \u002F\u002F kick to login\n  }\n  return children; \u002F\u002F authenticated — render the page\n}\n```\n\n**Rule of thumb:** A protected route is just a conditional render — show the page or\nredirect; the *real* security still lives on the server.\n",{"id":2559,"difficulty":96,"q":2560,"a":2561},"require-auth-wrapper","How do you implement a RequireAuth wrapper component using React Router v6's Navigate?","In React Router v6 the `\u003CNavigate>` component performs a **declarative redirect**.\nA `RequireAuth` wrapper checks auth state and either renders `\u003COutlet \u002F>` (for\nlayout-route usage) or `children` (for direct wrapping).\n\n```jsx\nimport { Navigate, Outlet, useLocation } from 'react-router-dom';\nimport { useAuth } from '..\u002Fcontext\u002FAuthContext';\n\n\u002F\u002F Layout-route variant — works with nested \u003CRoute> trees\nexport function RequireAuth() {\n  const { user } = useAuth();\n  const location = useLocation(); \u002F\u002F capture current path\n\n  if (!user) {\n    \u002F\u002F Pass current location so login can redirect back after success\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n\n  return \u003COutlet \u002F>; \u002F\u002F render matched child route\n}\n```\n\nIn the router config, nest protected routes under `RequireAuth`:\n\n```jsx\n\u003CRoute element={\u003CRequireAuth \u002F>}>\n  \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n  \u003CRoute path=\"\u002Fsettings\" element={\u003CSettings \u002F>} \u002F>\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Use the **layout-route** (`\u003COutlet \u002F>`) pattern rather than\nwrapping every `\u003CRoute element>` individually — one guard covers all children.\n",{"id":2563,"difficulty":121,"q":2564,"a":2565},"preserve-intended-destination","How do you preserve the intended destination so users land on the right page after login?","When unauthenticated users hit a protected URL you pass the current **location** in\n`state` on the redirect. After a successful login you read `location.state.from` and\ncall `navigate()` to send them there instead of a hard-coded fallback.\n\n```jsx\n\u002F\u002F 1. Guard passes current location in redirect state\nfunction RequireAuth() {\n  const { user } = useAuth();\n  const location = useLocation();\n  if (!user) {\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F 2. Login page reads state.from after successful login\nfunction LoginPage() {\n  const { login } = useAuth();\n  const navigate = useNavigate();\n  const location = useLocation();\n  const from = location.state?.from?.pathname ?? '\u002Fdashboard'; \u002F\u002F fallback\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    await login(formData); \u002F\u002F authenticate\n    navigate(from, { replace: true }); \u002F\u002F go to original destination\n  }\n  \u002F\u002F ...render form\n}\n```\n\n**Rule of thumb:** Always supply a **fallback path** (`?? '\u002Fdashboard'`) — if the\nuser bookmarks `\u002Flogin` directly, `state` is undefined.\n",{"id":2567,"difficulty":121,"q":2568,"a":2569},"rbac-route-protection","How do you implement role-based access control (RBAC) in React Router v6 routes?","Extend the `RequireAuth` pattern with an `allowedRoles` prop. The guard checks both\n**authentication** and **authorization** before rendering.\n\n```jsx\nimport { Navigate, Outlet, useLocation } from 'react-router-dom';\nimport { useAuth } from '..\u002Fcontext\u002FAuthContext';\n\n\u002F\u002F allowedRoles: string[] — e.g. ['admin', 'editor']\nfunction RequireRole({ allowedRoles }) {\n  const { user } = useAuth();\n  const location = useLocation();\n\n  if (!user) {\n    \u002F\u002F Not logged in → redirect to login\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n\n  if (!allowedRoles.includes(user.role)) {\n    \u002F\u002F Logged in but wrong role → show 403\n    return \u003CNavigate to=\"\u002F403\" replace \u002F>;\n  }\n\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Router config\n\u003CRoute element={\u003CRequireRole allowedRoles={['admin']} \u002F>}>\n  \u003CRoute path=\"\u002Fadmin\" element={\u003CAdminPanel \u002F>} \u002F>\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Always separate the two checks — unauthenticated users should\nsee the **login page**, not a 403; authorized users with the wrong role should see\n**403**, not the login page.\n",{"id":2571,"difficulty":121,"q":2572,"a":2573},"protecting-nested-routes","How do you protect an entire branch of nested routes with a single layout route guard?","React Router v6's **layout route** pattern (a `\u003CRoute>` with no `path` but with an\n`element`) lets you wrap a whole subtree. Combine this with `RequireAuth` to cover\nall descendants at once.\n\n```jsx\n\u003CRoutes>\n  {\u002F* Public routes *\u002F}\n  \u003CRoute path=\"\u002F\" element={\u003CHome \u002F>} \u002F>\n  \u003CRoute path=\"\u002Flogin\" element={\u003CLoginPage \u002F>} \u002F>\n\n  {\u002F* Protected subtree — single guard for all children *\u002F}\n  \u003CRoute element={\u003CRequireAuth \u002F>}>\n    \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n    \u003CRoute path=\"\u002Fprofile\" element={\u003CProfile \u002F>} \u002F>\n\n    {\u002F* Doubly-nested admin section with role check *\u002F}\n    \u003CRoute element={\u003CRequireRole allowedRoles={['admin']} \u002F>}>\n      \u003CRoute path=\"\u002Fadmin\" element={\u003CAdminPanel \u002F>} \u002F>\n      \u003CRoute path=\"\u002Fadmin\u002Fusers\" element={\u003CUserManager \u002F>} \u002F>\n    \u003C\u002FRoute>\n  \u003C\u002FRoute>\n\u003C\u002FRoutes>\n```\n\n**Rule of thumb:** Think of layout routes as **middleware layers** — stack them to\ncompose auth + role checks without repeating logic on each leaf route.\n",{"id":2575,"difficulty":121,"q":2576,"a":2577},"lazy-loading-protected-routes","How do you lazy-load protected route components with React.lazy and Suspense?","Use `React.lazy` to **code-split** heavy protected pages so their JS chunk is only\ndownloaded after the guard confirms auth. Wrap the lazy import in `\u003CSuspense>` inside\n(or outside) the `RequireAuth` element.\n\n```jsx\nimport React, { Suspense } from 'react';\nimport { Route, Routes } from 'react-router-dom';\nimport { RequireAuth } from '.\u002Fguards\u002FRequireAuth';\n\n\u002F\u002F Lazy imports — bundle splits here\nconst Dashboard = React.lazy(() => import('.\u002Fpages\u002FDashboard'));\nconst AdminPanel = React.lazy(() => import('.\u002Fpages\u002FAdminPanel'));\n\nfunction AppRoutes() {\n  return (\n    \u002F\u002F Suspense can live here to cover all lazy routes at once\n    \u003CSuspense fallback={\u003Cdiv>Loading…\u003C\u002Fdiv>}>\n      \u003CRoutes>\n        \u003CRoute element={\u003CRequireAuth \u002F>}>\n          \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n          \u003CRoute path=\"\u002Fadmin\" element={\u003CAdminPanel \u002F>} \u002F>\n        \u003C\u002FRoute>\n      \u003C\u002FRoutes>\n    \u003C\u002FSuspense>\n  );\n}\n```\n\n**Rule of thumb:** Place `\u003CSuspense>` **outside** `\u003CRoutes>` to handle all lazy\nroutes uniformly; only drop it inside a specific `\u003CRoute>` if you need per-page\nloading UI.\n",{"id":2579,"difficulty":208,"q":2580,"a":2581},"token-storage-security","Compare localStorage, cookies, and in-memory storage for auth tokens and their implications for routing guards.","The storage choice affects how the auth guard reads the token on each render and\nwhat attack surface you expose.\n\n| Storage | XSS risk | CSRF risk | Survives refresh | Notes |\n|---|---|---|---|---|\n| `localStorage` | High — JS-readable | None | Yes | Avoid for sensitive tokens |\n| `httpOnly` cookie | None — server-set | Medium | Yes | Best for session tokens |\n| Memory (React state) | Low | None | No | Requires silent-refresh |\n\n```jsx\n\u002F\u002F In-memory pattern — token lives only in AuthContext state\nconst AuthContext = createContext(null);\n\nexport function AuthProvider({ children }) {\n  const [user, setUser] = useState(null); \u002F\u002F cleared on tab close\n\n  \u002F\u002F On mount, attempt a silent refresh via httpOnly refresh-token cookie\n  useEffect(() => {\n    api.post('\u002Fauth\u002Frefresh')\n      .then(({ data }) => setUser(data.user)) \u002F\u002F restore session\n      .catch(() => setUser(null)); \u002F\u002F no valid cookie → stay logged out\n  }, []);\n\n  return \u003CAuthContext.Provider value={{ user, setUser }}>{children}\u003C\u002FAuthContext.Provider>;\n}\n```\n\n**Rule of thumb:** Store the **access token in memory**, set the **refresh token as\nan httpOnly cookie** — you get XSS-safety for the short-lived token and automatic\nsession persistence without `localStorage`.\n",{"id":2583,"difficulty":121,"q":2584,"a":2585},"optimistic-auth-loading-state","What is the difference between optimistic auth and a loading-state guard, and which should you use?","On initial page load the auth context hasn't yet rehydrated (e.g., the silent-refresh\ncall is in-flight). Two strategies exist:\n\n**Optimistic auth** assumes the user *is* logged in until proven otherwise — renders\nthe protected page immediately, then potentially redirects. Can cause a **flash** of\nprotected content.\n\n**Loading-state guard** holds rendering until auth is confirmed. Prevents flashing\nbut shows a spinner on every cold load.\n\n```jsx\nfunction RequireAuth() {\n  const { user, loading } = useAuth();\n  const location = useLocation();\n\n  if (loading) {\n    \u002F\u002F Wait for silent-refresh before deciding\n    return \u003Cdiv className=\"spinner\" aria-label=\"Checking auth…\" \u002F>;\n  }\n\n  if (!user) {\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n\n  return \u003COutlet \u002F>;\n}\n```\n\n**Rule of thumb:** Always use the **loading-state guard** — a brief spinner is a\nfar better UX than flashing private data at unauthenticated users.\n",{"id":2587,"difficulty":208,"q":2588,"a":2589},"token-expiry-interceptor","How do you handle token expiry mid-session without forcing the user to re-login?","Set up an **Axios\u002FFetch interceptor** that detects a 401 response, silently requests\na new access token using the refresh-token cookie, retries the original request, and\nonly redirects to login when the refresh itself fails.\n\n```jsx\n\u002F\u002F api.js — Axios instance with interceptor\nimport axios from 'axios';\n\nconst api = axios.create({ baseURL: '\u002Fapi', withCredentials: true });\n\nlet refreshPromise = null; \u002F\u002F prevent concurrent refresh calls\n\napi.interceptors.response.use(\n  res => res,\n  async error => {\n    const original = error.config;\n    if (error.response?.status === 401 && !original._retry) {\n      original._retry = true;\n      \u002F\u002F Deduplicate: reuse an in-flight refresh\n      refreshPromise = refreshPromise ?? api.post('\u002Fauth\u002Frefresh').finally(() => {\n        refreshPromise = null;\n      });\n      await refreshPromise;        \u002F\u002F wait for new token\n      return api(original);        \u002F\u002F retry original request\n    }\n    \u002F\u002F Refresh failed → redirect to login\n    window.location.href = '\u002Flogin';\n    return Promise.reject(error);\n  }\n);\n```\n\n**Rule of thumb:** Use a **single shared refresh promise** to prevent multiple\nparallel 401 responses from each triggering their own refresh race.\n",{"id":2591,"difficulty":121,"q":2592,"a":2593},"auth-context-pattern","Describe the AuthContext + useAuth hook pattern and why it's preferred for protecting routes.","**AuthContext** stores auth state (user, loading, login, logout) in React context so\nany component in the tree — including route guards — can read it without prop-drilling.\nThe `useAuth` hook wraps `useContext` and throws early if used outside the provider.\n\n```jsx\n\u002F\u002F context\u002FAuthContext.jsx\nimport { createContext, useContext, useState, useEffect } from 'react';\n\nconst AuthContext = createContext(null);\n\nexport function AuthProvider({ children }) {\n  const [user, setUser] = useState(null);\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() => {\n    \u002F\u002F Restore session on mount\n    api.get('\u002Fauth\u002Fme')\n      .then(({ data }) => setUser(data))\n      .catch(() => setUser(null))\n      .finally(() => setLoading(false)); \u002F\u002F stop spinner\n  }, []);\n\n  const login = async (creds) => {\n    const { data } = await api.post('\u002Fauth\u002Flogin', creds);\n    setUser(data.user); \u002F\u002F update global state\n  };\n\n  const logout = async () => {\n    await api.post('\u002Fauth\u002Flogout');\n    setUser(null);\n  };\n\n  return (\n    \u003CAuthContext.Provider value={{ user, loading, login, logout }}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  );\n}\n\n\u002F\u002F Enforces provider presence\nexport function useAuth() {\n  const ctx = useContext(AuthContext);\n  if (!ctx) throw new Error('useAuth must be used inside AuthProvider');\n  return ctx;\n}\n```\n\n**Rule of thumb:** Throw in `useAuth` when the context is null — it turns a silent\nwrong-render into an obvious developer error.\n",{"id":2595,"difficulty":121,"q":2596,"a":2597},"route-level-vs-component-level","When should you use route-level guards versus component-level access checks?","**Route-level guards** (`RequireAuth` layout routes) handle coarse-grained access —\nthey prevent entire pages from rendering. **Component-level guards** handle\nfine-grained UI elements within a page (e.g., an \"Edit\" button visible only to\nadmins).\n\n```jsx\n\u002F\u002F Route-level guard — whole page blocked for non-admins\n\u003CRoute element={\u003CRequireRole allowedRoles={['admin']} \u002F>}>\n  \u003CRoute path=\"\u002Fadmin\" element={\u003CAdminDashboard \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F Component-level guard — same page, conditional UI\nfunction ArticlePage() {\n  const { user } = useAuth();\n  return (\n    \u003Carticle>\n      \u003Ch1>{article.title}\u003C\u002Fh1>\n      \u003Cp>{article.body}\u003C\u002Fp>\n      {\u002F* Only editors see the Edit button *\u002F}\n      {user?.role === 'editor' && (\n        \u003Cbutton onClick={handleEdit}>Edit\u003C\u002Fbutton>\n      )}\n    \u003C\u002Farticle>\n  );\n}\n```\n\n**Rule of thumb:** Use **route-level** guards for page access, **component-level**\nguards for UI elements — never rely on hiding UI elements as the sole security\nmeasure.\n",{"id":2599,"difficulty":121,"q":2600,"a":2601},"testing-protected-routes","How do you test protected routes by mocking the auth context?","Render a custom wrapper that supplies a mock `AuthContext` value. This lets you test\nboth the authenticated and unauthenticated branches without a real auth server.\n\n```jsx\n\u002F\u002F test\u002Futils.jsx\nimport { render } from '@testing-library\u002Freact';\nimport { MemoryRouter } from 'react-router-dom';\nimport { AuthContext } from '..\u002Fcontext\u002FAuthContext';\n\nexport function renderWithAuth(ui, { user = null, route = '\u002F' } = {}) {\n  return render(\n    \u002F\u002F MemoryRouter lets us set the initial URL\n    \u003CMemoryRouter initialEntries={[route]}>\n      \u003CAuthContext.Provider value={{ user, loading: false }}>\n        {ui}\n      \u003C\u002FAuthContext.Provider>\n    \u003C\u002FMemoryRouter>\n  );\n}\n\n\u002F\u002F dashboard.test.jsx\nimport { screen } from '@testing-library\u002Freact';\nimport { Routes, Route } from 'react-router-dom';\nimport { RequireAuth } from '..\u002Fguards\u002FRequireAuth';\nimport { Dashboard } from '..\u002Fpages\u002FDashboard';\n\ntest('redirects to login when unauthenticated', () => {\n  renderWithAuth(\n    \u003CRoutes>\n      \u003CRoute path=\"\u002Flogin\" element={\u003Cp>Login page\u003C\u002Fp>} \u002F>\n      \u003CRoute element={\u003CRequireAuth \u002F>}>\n        \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n      \u003C\u002FRoute>\n    \u003C\u002FRoutes>,\n    { user: null, route: '\u002Fdashboard' } \u002F\u002F no user\n  );\n  expect(screen.getByText('Login page')).toBeInTheDocument();\n});\n```\n\n**Rule of thumb:** Use `MemoryRouter` (not `BrowserRouter`) in tests — you control\nthe initial URL without touching `window.location`.\n",{"id":2603,"difficulty":208,"q":2604,"a":2605},"server-vs-client-protection","What are the trade-offs between client-side route protection and server-side protection?","**Client-side protection** (React Router guards) is UI-only — it prevents rendering\nthe component but the API endpoints are still reachable by anyone with network tools.\n**Server-side protection** validates the token on every API call and is the true\nsecurity boundary.\n\n```jsx\n\u002F\u002F Client-side guard — UX only, not a security boundary\nfunction RequireAuth() {\n  const { user } = useAuth();\n  if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Server-side guard (Express example) — real security\nfunction authMiddleware(req, res, next) {\n  const token = req.cookies.accessToken;\n  try {\n    req.user = jwt.verify(token, process.env.JWT_SECRET); \u002F\u002F throws if invalid\n    next();\n  } catch {\n    res.status(401).json({ error: 'Unauthorized' }); \u002F\u002F API rejects bad token\n  }\n}\n\n\u002F\u002F Every protected API route uses the middleware\nrouter.get('\u002Fapi\u002Fdashboard-data', authMiddleware, getDashboardData);\n```\n\n**Rule of thumb:** Client-side guards protect **UX**; server-side guards protect\n**data** — you need both, and the server is the only one that matters for security.\n",{"id":578,"difficulty":96,"q":2607,"a":2608},"Why should you use replace={true} on the Navigate redirect in an auth guard?","Without `replace`, the redirect adds the protected URL to the **browser history\nstack**. After login the user presses Back, lands on the protected page URL in\nhistory, gets redirected again, and is stuck in a loop. Using `replace` **overwrites**\nthe history entry so the Back button goes to wherever they came from before the\nprotected URL.\n\n```jsx\nfunction RequireAuth() {\n  const { user } = useAuth();\n  const location = useLocation();\n\n  if (!user) {\n    return (\n      \u003CNavigate\n        to=\"\u002Flogin\"\n        state={{ from: location }}\n        replace   \u002F\u002F ← replaces instead of pushing — prevents back-button loop\n      \u002F>\n    );\n  }\n  return \u003COutlet \u002F>;\n}\n```\n\n**Rule of thumb:** Always pass `replace` on auth redirects to avoid **back-button\nloops** — it's a one-word fix with a significant UX impact.\n",{"id":2610,"difficulty":96,"q":2611,"a":2612},"public-only-routes","How do you create a \"public-only\" route that redirects authenticated users away from the login page?","The inverse of `RequireAuth` — a **GuestOnly** guard redirects authenticated users\nwho try to visit `\u002Flogin` or `\u002Fregister` to the dashboard instead of showing them\na form they don't need.\n\n```jsx\nfunction GuestOnly() {\n  const { user, loading } = useAuth();\n  const location = useLocation();\n\n  if (loading) return \u003Cdiv>Loading…\u003C\u002Fdiv>;\n\n  if (user) {\n    \u002F\u002F Authenticated user tried to reach \u002Flogin — send them home\n    const destination = location.state?.from?.pathname ?? '\u002Fdashboard';\n    return \u003CNavigate to={destination} replace \u002F>;\n  }\n\n  return \u003COutlet \u002F>; \u002F\u002F not logged in — show login\u002Fregister\n}\n\n\u002F\u002F Router config\n\u003CRoute element={\u003CGuestOnly \u002F>}>\n  \u003CRoute path=\"\u002Flogin\" element={\u003CLoginPage \u002F>} \u002F>\n  \u003CRoute path=\"\u002Fregister\" element={\u003CRegisterPage \u002F>} \u002F>\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Pair every `RequireAuth` guard with a `GuestOnly` guard — without\nit, logged-in users see the login form and get confused.\n",{"id":2614,"difficulty":121,"q":2615,"a":2616},"persisting-auth-across-refresh","How do you persist authentication state across a full page refresh in a React SPA?","On a hard refresh React state is wiped. To restore auth you have two options:\n(1) read a token from `localStorage` synchronously on mount, or (2) fire a\n**silent-refresh** API call on mount using an httpOnly refresh-token cookie.\nOption 2 is more secure.\n\n```jsx\nexport function AuthProvider({ children }) {\n  const [user, setUser] = useState(null);\n  const [loading, setLoading] = useState(true); \u002F\u002F true until we know\n\n  useEffect(() => {\n    \u002F\u002F Silent refresh — relies on httpOnly cookie sent automatically\n    api.post('\u002Fauth\u002Frefresh')\n      .then(({ data }) => {\n        setUser(data.user);      \u002F\u002F restore user object\n      })\n      .catch(() => {\n        setUser(null);           \u002F\u002F no valid cookie → stay logged out\n      })\n      .finally(() => {\n        setLoading(false);       \u002F\u002F guards can now make a decision\n      });\n  }, []); \u002F\u002F run once on mount\n\n  return (\n    \u003CAuthContext.Provider value={{ user, loading }}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  );\n}\n```\n\n**Rule of thumb:** Keep `loading: true` until the refresh attempt resolves — every\nguard in the app blocks on this flag, preventing a premature redirect to `\u002Flogin`.\n",{"id":2618,"difficulty":208,"q":2619,"a":2620},"multiple-guards-composition","How do you compose multiple guards (auth + subscription + feature flag) on a single route?","Nest layout routes — each wraps the next, and all must pass before the leaf renders.\nThis keeps each guard **single-responsibility** and independently testable.\n\n```jsx\n\u002F\u002F Each guard does one job and delegates via \u003COutlet \u002F>\nfunction RequireSubscription() {\n  const { user } = useAuth();\n  if (!user?.subscription?.active) {\n    return \u003CNavigate to=\"\u002Fupgrade\" replace \u002F>;\n  }\n  return \u003COutlet \u002F>;\n}\n\nfunction RequireFeatureFlag({ flag }) {\n  const { flags } = useFeatureFlags();\n  if (!flags[flag]) {\n    return \u003CNavigate to=\"\u002Fcoming-soon\" replace \u002F>;\n  }\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Router — guards stack innermost-last\n\u003CRoute element={\u003CRequireAuth \u002F>}>             {\u002F* 1st: auth *\u002F}\n  \u003CRoute element={\u003CRequireSubscription \u002F>}>   {\u002F* 2nd: paid plan *\u002F}\n    \u003CRoute element={\u003CRequireFeatureFlag flag=\"ai-tools\" \u002F>}> {\u002F* 3rd: flag *\u002F}\n      \u003CRoute path=\"\u002Fai-tools\" element={\u003CAiTools \u002F>} \u002F>\n    \u003C\u002FRoute>\n  \u003C\u002FRoute>\n\u003C\u002FRoute>\n```\n\n**Rule of thumb:** Compose guards by **nesting layout routes** rather than combining\nlogic in one mega-guard — each layer stays readable and individually unit-testable.\n",{"id":2622,"difficulty":121,"q":2623,"a":2624},"redirect-loop-debugging","What causes an infinite redirect loop in protected routes and how do you fix it?","A redirect loop happens when the guard sends the user to `\u002Flogin`, but `\u002Flogin` is\nalso behind the guard (or the auth state never resolves to `true`), so the guard\nredirects again immediately.\n\nCommon causes and fixes:\n\n```jsx\n\u002F\u002F BUG: \u002Flogin is inside RequireAuth — redirects loop forever\n\u003CRoute element={\u003CRequireAuth \u002F>}>\n  \u003CRoute path=\"\u002Flogin\" element={\u003CLoginPage \u002F>} \u002F> {\u002F* ← wrong! *\u002F}\n  \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F>\n\u003C\u002FRoute>\n\n\u002F\u002F FIX: \u002Flogin must be outside the guard\n\u003CRoutes>\n  \u003CRoute path=\"\u002Flogin\" element={\u003CLoginPage \u002F>} \u002F>  {\u002F* public *\u002F}\n\n  \u003CRoute element={\u003CRequireAuth \u002F>}>\n    \u003CRoute path=\"\u002Fdashboard\" element={\u003CDashboard \u002F>} \u002F> {\u002F* protected *\u002F}\n  \u003C\u002FRoute>\n\u003C\u002FRoutes>\n\n\u002F\u002F BUG 2: guard doesn't wait for loading — user is null on first render\n\u002F\u002F even when a valid refresh cookie exists → immediate redirect to login\nfunction RequireAuth() {\n  const { user, loading } = useAuth();\n  if (loading) return null; \u002F\u002F ← wait before deciding\n  if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n  return \u003COutlet \u002F>;\n}\n```\n\n**Rule of thumb:** If you see a redirect loop, check two things: is the redirect\n**target outside the guard**, and is the guard **waiting for the loading flag**?\n",{"id":2626,"difficulty":96,"q":2627,"a":2628},"useNavigate-vs-navigate-component","When should you use the Navigate component versus the useNavigate hook in auth flows?","`\u003CNavigate>` is a **declarative** redirect that happens during render — ideal for\nguards that decide synchronously (user is or isn't logged in right now).\n`useNavigate()` returns an **imperative** `navigate()` function — ideal for\nredirecting *after* an async operation (login form submit, token refresh).\n\n```jsx\n\u002F\u002F Declarative — fires during render, perfect for guards\nfunction RequireAuth() {\n  const { user } = useAuth();\n  if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>; \u002F\u002F renders a redirect\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Imperative — fires after async logic, perfect for post-login redirect\nfunction LoginPage() {\n  const { login } = useAuth();\n  const navigate = useNavigate();\n  const location = useLocation();\n  const from = location.state?.from?.pathname ?? '\u002Fdashboard';\n\n  async function handleSubmit(credentials) {\n    await login(credentials);       \u002F\u002F async: wait for server response\n    navigate(from, { replace: true }); \u002F\u002F then redirect\n  }\n}\n```\n\n**Rule of thumb:** Use `\u003CNavigate>` in **guards** (render-time decision), use\n`useNavigate` in **event handlers** (post-async decision).\n",{"id":2630,"difficulty":121,"q":2631,"a":2632},"404-vs-403-handling","How should a protected-route guard distinguish between a 404 (route not found) and a 403 (forbidden)?","A **404** means the URL doesn't match any route. A **403** means the URL matched but\nthe user lacks permission. Conflating them leaks information (you reveal the route\nexists to unauthorized users). In practice, both are acceptable — many apps return\na 404 for unauthorized routes to avoid enumeration.\n\n```jsx\nfunction RequireRole({ allowedRoles }) {\n  const { user } = useAuth();\n  const location = useLocation();\n\n  if (!user) {\n    \u002F\u002F Not logged in → login (don't reveal the page exists)\n    return \u003CNavigate to=\"\u002Flogin\" state={{ from: location }} replace \u002F>;\n  }\n\n  if (!allowedRoles.includes(user.role)) {\n    \u002F\u002F Logged in, wrong role — choose 403 or 404 based on policy\n    return \u003CNavigate to=\"\u002F403\" replace \u002F>; \u002F\u002F explicit \"forbidden\" page\n    \u002F\u002F OR: return \u003CNavigate to=\"\u002F404\" replace \u002F>; \u002F\u002F stealth — hides route existence\n  }\n\n  return \u003COutlet \u002F>;\n}\n\n\u002F\u002F Catch-all 404 must be the last route in the tree\n\u003CRoute path=\"*\" element={\u003CNotFoundPage \u002F>} \u002F>\n```\n\n**Rule of thumb:** Send unauthenticated users to **login**, send authenticated but\nunauthorized users to **403** (or 404 for sensitive admin routes) — keep the\ndistinction deliberate and documented.\n",{"description":94},"React Router v6 protected routes interview questions — RequireAuth wrapper, Navigate redirect, role-based access, auth context, token storage, and redirect-after-login patterns.","react\u002Frouting\u002Fprotected-routes","WBfNu4OmQJrFDE-JcwSQzTUWre5iGF2lnnAP77GmAOE",{"id":2638,"title":2639,"body":2640,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":2644,"navigation":100,"order":46,"path":2646,"questions":2647,"questionsCount":1237,"related":169,"seo":2704,"seoDescription":2705,"stem":2706,"subtopic":2707,"topic":36,"topicSlug":38,"updated":420,"__hash__":2708},"qa\u002Freact\u002Fstate-and-data-flow\u002Fprop-drilling-composition.md","Prop Drilling Composition",{"type":91,"value":2641,"toc":2642},[],{"title":94,"searchDepth":11,"depth":11,"links":2643},[],{"subtopicSlug":2645},"prop-drilling-composition","\u002Freact\u002Fstate-and-data-flow\u002Fprop-drilling-composition",[2648,2652,2656,2660,2664,2668,2672,2676,2680,2684,2688,2692,2696,2700],{"id":2649,"difficulty":96,"q":2650,"a":2651},"prop-drilling-what","What is prop drilling in React?","**Prop drilling** is the practice of passing data through multiple\nintermediate components that **don't need it themselves**, just so it\ncan reach a deeply nested consumer.\n\n```jsx\n\u002F\u002F data flows through App → Page → Section → Widget → Button\n\u002F\u002F Page, Section, and Widget never use `user`, they just pass it on\nfunction App()     { return \u003CPage user={user} \u002F> }\nfunction Page({user})    { return \u003CSection user={user} \u002F> }\nfunction Section({user}) { return \u003CWidget user={user} \u002F> }\nfunction Widget({user})  { return \u003CButton user={user} \u002F> }\nfunction Button({user})  { return \u003Cspan>{user.name}\u003C\u002Fspan> }\n```\n\nThe intermediate components become tightly coupled to data they don't\nown, making refactoring painful.\n\n**Rule of thumb:** If a prop passes through two or more components that\ndon't use it, you have a prop drilling problem worth addressing.\n",{"id":2653,"difficulty":96,"q":2654,"a":2655},"prop-drilling-problems","What are the main problems caused by prop drilling?","1. **Coupling** — intermediate components must accept and forward props\n   they don't care about, creating unnecessary dependencies.\n2. **Refactor friction** — adding, removing, or renaming a prop means\n   updating every intermediate layer.\n3. **Readability** — component signatures grow bloated with pass-through\n   props, obscuring the component's real interface.\n4. **Testing burden** — intermediate components must be tested with\n   props they don't use, just to satisfy type signatures.\n\n```jsx\n\u002F\u002F Every time the shape of `user` changes, all three layers must be\n\u002F\u002F updated — even Page and Section which never read user directly\nfunction Page({ user, theme, locale, featureFlags }) { ... }\n```\n\n**Rule of thumb:** The deeper the tree and the more layers a prop\ncrosses, the more painful prop drilling becomes.\n",{"id":2657,"difficulty":121,"q":2658,"a":2659},"prop-drilling-composition-fix","How can component composition solve prop drilling without using Context?","Pass **pre-rendered children** (JSX elements) down instead of raw data.\nThe top-level component builds the element with full access to its own\nscope; intermediate components just place it.\n\n```jsx\n\u002F\u002F Instead of threading `user` through Page → Section → Widget:\n\nfunction App() {\n  return (\n    \u003CPage>\n      \u003CSection>\n        \u003CWidget>\n          \u003CButton user={user} \u002F>   {\u002F* App has direct access to user *\u002F}\n        \u003C\u002FWidget>\n      \u003C\u002FSection>\n    \u003C\u002FPage>\n  )\n}\n\n\u002F\u002F Intermediates accept children and don't know about user\nfunction Page({ children })    { return \u003Cmain>{children}\u003C\u002Fmain> }\nfunction Section({ children }) { return \u003Csection>{children}\u003C\u002Fsection> }\nfunction Widget({ children })  { return \u003Cdiv>{children}\u003C\u002Fdiv> }\n```\n\nReact calls this **inversion of control** — the parent controls what\nthe children render.\n\n**Rule of thumb:** Try the `children` pattern before reaching for\nContext — it often eliminates drilling with zero new abstractions.\n",{"id":2661,"difficulty":96,"q":2662,"a":2663},"prop-drilling-children-prop","What is the `children` prop and how does it enable composition?","`children` is the implicit prop React passes for any JSX content placed\nbetween a component's opening and closing tags. Components that render\n`{children}` act as **layout containers** — they don't need to know\nwhat's inside.\n\n```jsx\nfunction Card({ title, children }) {\n  return (\n    \u003Cdiv className=\"card\">\n      \u003Ch2>{title}\u003C\u002Fh2>\n      \u003Cdiv className=\"card-body\">{children}\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage\n\u003CCard title=\"Profile\">\n  \u003CAvatar user={user} \u002F>\n  \u003CBio text={user.bio} \u002F>\n\u003C\u002FCard>\n```\n\n`Card` renders the user-specific components without knowing anything\nabout `user` itself.\n\n**Rule of thumb:** If a component is a structural wrapper (card, modal,\nlayout), always accept `children` rather than a specific content prop.\n",{"id":2665,"difficulty":121,"q":2666,"a":2667},"prop-drilling-slots","What is the \"slot\" pattern in React and how does it differ from just using children?","The slot pattern uses **named props** (each holding a JSX element) to\nplace content in specific regions — like a header slot, footer slot, and\nbody slot — without the component needing to know about the data inside.\n\n```jsx\nfunction Layout({ header, sidebar, children }) {\n  return (\n    \u003Cdiv className=\"layout\">\n      \u003Cheader>{header}\u003C\u002Fheader>\n      \u003Caside>{sidebar}\u003C\u002Faside>\n      \u003Cmain>{children}\u003C\u002Fmain>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Caller builds each slot with its own data\n\u003CLayout\n  header={\u003CNavBar user={user} \u002F>}\n  sidebar={\u003CUserMenu user={user} \u002F>}\n>\n  \u003CFeed posts={posts} \u002F>\n\u003C\u002FLayout>\n```\n\n`Layout` remains generic; the caller composes what goes into each slot.\n\n**Rule of thumb:** Use named slots when a layout component has multiple\ndistinct areas that need different content.\n",{"id":2669,"difficulty":121,"q":2670,"a":2671},"prop-drilling-context-solution","When does composition not solve prop drilling and Context becomes necessary?","Composition fails (or becomes awkward) when:\n- The consuming component is **many levels deep** and you would need to\n  thread JSX through too many layers to compose it at the top.\n- The consuming component is in a **third-party library** or a **route**\n  you don't control.\n- The data is needed by **many unrelated subtrees** (e.g. current user,\n  theme, locale).\n\n```jsx\n\u002F\u002F If Button is inside a deeply nested third-party table cell,\n\u002F\u002F you can't easily pass it as a child from the top.\n\u002F\u002F Context is the right tool here.\nconst UserContext = createContext(null)\n\nfunction App() {\n  return (\n    \u003CUserContext.Provider value={user}>\n      \u003CThirdPartyTable columns={columns} \u002F>\n    \u003C\u002FUserContext.Provider>\n  )\n}\n\nfunction CustomCell() {\n  const user = useContext(UserContext)\n  return \u003CButton>{user.name}\u003C\u002FButton>\n}\n```\n\n**Rule of thumb:** Try composition first. Reach for Context when the\nconsumer and the data owner are architecturally disconnected.\n",{"id":2673,"difficulty":121,"q":2674,"a":2675},"prop-drilling-render-props","What are render props and how do they help with composition?","A **render prop** is a prop whose value is a function that returns JSX.\nThe component calls the function at render time, passing internal state\nor logic to the caller.\n\n```jsx\nfunction MouseTracker({ render }) {\n  const [pos, setPos] = useState({ x: 0, y: 0 })\n\n  return (\n    \u003Cdiv onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>\n      {render(pos)}    {\u002F* caller decides what to show *\u002F}\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage — no prop drilling; the caller gets the data it needs\n\u003CMouseTracker render={({ x, y }) => \u003CCursor x={x} y={y} \u002F>} \u002F>\n```\n\nRender props share logic without a component hierarchy, but they can\nlead to deeply nested JSX (\"callback hell\"). Custom hooks have largely\nreplaced them for logic sharing.\n\n**Rule of thumb:** Prefer custom hooks for sharing stateful logic; use\nrender props only when you need to inject JSX into a component that\ncontrols lifecycle.\n",{"id":2677,"difficulty":121,"q":2678,"a":2679},"prop-drilling-hoc","How do Higher-Order Components (HOCs) relate to prop drilling?","An **HOC** is a function that takes a component and returns a new\ncomponent with extra props injected. They were the pre-hooks solution\nfor sharing logic without drilling, but they add indirection and can\nconflict with prop names.\n\n```jsx\nfunction withUser(Component) {\n  return function WrappedComponent(props) {\n    const user = useContext(UserContext)\n    return \u003CComponent {...props} user={user} \u002F>\n  }\n}\n\nconst ProfileWithUser = withUser(Profile)\n\u002F\u002F Profile receives user without the caller drilling it\n```\n\nHOCs are still used in third-party libraries (React-Redux's `connect`)\nbut custom hooks are preferred for new code.\n\n**Rule of thumb:** Write a custom hook first. Only reach for an HOC\nwhen the consuming component is a class component or you need to inject\ninto a component you don't own.\n",{"id":2681,"difficulty":121,"q":2682,"a":2683},"prop-drilling-avoid-global","Is it always wrong to pass a prop through an intermediate component?","No. Passing props **one level down** is perfectly fine and is often the\nclearest approach. Prop drilling is only a problem when it spans many\nlayers **and** the intermediate components have no other reason to know\nabout the prop.\n\n```jsx\n\u002F\u002F Fine — one level, clear intent\nfunction ProductPage({ product }) {\n  return \u003CProductCard product={product} \u002F>\n}\n\n\u002F\u002F Problem — three layers, none of them use `user`\nfunction ProductPage({ product, user }) {\n  return \u003CProductCard product={product} user={user} \u002F>\n}\nfunction ProductCard({ product, user }) {\n  return \u003CSaveButton user={user} \u002F>\n}\nfunction SaveButton({ user }) {\n  return \u003Cbutton disabled={!user.isPremium}>Save\u003C\u002Fbutton>\n}\n```\n\n**Rule of thumb:** One level is fine. Two levels — consider composition.\nThree or more — seriously evaluate Context or composition.\n",{"id":2685,"difficulty":121,"q":2686,"a":2687},"prop-drilling-colocate","How can co-locating state close to where it's used reduce prop drilling?","If state is lifted higher than it needs to be, every component between\nthe owner and the consumer has to forward it. Moving state **down** to\nthe component that actually needs it eliminates the intermediate passes.\n\n```jsx\n\u002F\u002F ❌ modal state lives in App, drills through Header → Nav → MenuButton\nfunction App() {\n  const [isMenuOpen, setIsMenuOpen] = useState(false)\n  return \u003CHeader isMenuOpen={isMenuOpen} onToggle={setIsMenuOpen} \u002F>\n}\n\n\u002F\u002F ✅ menu state lives where it belongs — no drilling\nfunction Nav() {\n  const [isMenuOpen, setIsMenuOpen] = useState(false)\n  return \u003CMenuButton open={isMenuOpen} onToggle={setIsMenuOpen} \u002F>\n}\n```\n\nState co-location is the inverse of \"lift state up\" — lift only when\ntwo siblings need the same state; keep it local otherwise.\n\n**Rule of thumb:** Before reaching for Context, ask \"Can I just move\nthis state down to where it's used?\"\n",{"id":2689,"difficulty":208,"q":2690,"a":2691},"prop-drilling-zustand","How does a library like Zustand solve prop drilling compared to Context?","Zustand creates a **store outside React's component tree**. Any\ncomponent can subscribe directly with a selector and only re-renders\nwhen its selected slice changes — no Provider needed, no intermediate\nforwarding.\n\n```jsx\nimport { create } from 'zustand'\n\nconst useUserStore = create(set => ({\n  user: null,\n  setUser: user => set({ user }),\n}))\n\n\u002F\u002F Any component anywhere in the tree — no Provider, no drilling\nfunction Button() {\n  const user = useUserStore(state => state.user)\n  return \u003Cspan>{user?.name}\u003C\u002Fspan>\n}\n```\n\nUnlike Context, Zustand subscriptions are selector-scoped — a component\nonly re-renders when its selected slice actually changes.\n\n**Rule of thumb:** Add Zustand (or Redux Toolkit) when Context re-render\nperformance becomes a real problem or when you need devtools \u002F\nmiddleware.\n",{"id":2693,"difficulty":96,"q":2694,"a":2695},"prop-drilling-summary","What are the four main solutions to prop drilling, ordered by complexity?","1. **Co-location** — move state down so it lives closer to the consumer.\n2. **Composition \u002F children** — pass pre-rendered JSX or named slots so\n   intermediate components don't need to know about the data.\n3. **Context API** — share data across a subtree without prop passing,\n   best for infrequently changing global values.\n4. **External store** (Zustand, Redux) — selector-scoped subscriptions\n   for frequently changing data with many consumers.\n\n```\nCo-location   → simplest, no overhead\nComposition   → good DX, no new abstractions\nContext       → built-in, okay for infrequent updates\nStore library → scalable, selector performance, devtools\n```\n\n**Rule of thumb:** Solve at the lowest complexity level that works.\nDon't jump to Redux because you read about prop drilling; try\ncomposition first.\n",{"id":2697,"difficulty":96,"q":2698,"a":2699},"prop-drilling-types","What TypeScript pattern helps make prop-forwarding components less painful?","Use **rest props spread** (`...props`) combined with a specific type\nfor your own props, so the component forwards everything else without\nlisting every forwarded prop explicitly.\n\n```tsx\nimport { ButtonHTMLAttributes } from 'react'\n\ninterface IconButtonProps extends ButtonHTMLAttributes\u003CHTMLButtonElement> {\n  icon: string   \u002F\u002F own prop\n}\n\nfunction IconButton({ icon, ...rest }: IconButtonProps) {\n  return (\n    \u003Cbutton {...rest}>\n      \u003Ci className={`icon-${icon}`} \u002F>\n      {rest.children}\n    \u003C\u002Fbutton>\n  )\n}\n\n\u002F\u002F Caller can pass onClick, disabled, aria-label without drilling each one\n\u003CIconButton icon=\"save\" onClick={save} disabled={!dirty} \u002F>\n```\n\nThis pattern is especially useful for wrapper components around native\nelements.\n\n**Rule of thumb:** Extend `HTMLAttributes` and spread `...rest` for any\ncomponent that wraps a native element.\n",{"id":2701,"difficulty":208,"q":2702,"a":2703},"prop-drilling-compound","How does the compound component pattern reduce prop drilling inside a component family?","Compound components use **implicit Context** to share state between a\nparent and its child sub-components — the caller doesn't need to wire\nup any props between them.\n\n```jsx\nconst AccordionCtx = createContext(null)\n\nfunction Accordion({ children }) {\n  const [openId, setOpenId] = useState(null)\n  return (\n    \u003CAccordionCtx.Provider value={{ openId, setOpenId }}>\n      \u003Cdiv>{children}\u003C\u002Fdiv>\n    \u003C\u002FAccordionCtx.Provider>\n  )\n}\n\nAccordion.Item = function Item({ id, title, children }) {\n  const { openId, setOpenId } = useContext(AccordionCtx)\n  const isOpen = openId === id\n  return (\n    \u003Cdiv>\n      \u003Cbutton onClick={() => setOpenId(isOpen ? null : id)}>{title}\u003C\u002Fbutton>\n      {isOpen && \u003Cdiv>{children}\u003C\u002Fdiv>}\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage — no prop drilling between Accordion and Item\n\u003CAccordion>\n  \u003CAccordion.Item id=\"a\" title=\"Section A\">Content A\u003C\u002FAccordion.Item>\n  \u003CAccordion.Item id=\"b\" title=\"Section B\">Content B\u003C\u002FAccordion.Item>\n\u003C\u002FAccordion>\n```\n\n**Rule of thumb:** Use the compound component pattern for component\nfamilies (tabs, accordions, dropdowns) that need shared state but want\na clean JSX API.\n",{"description":94},"React prop drilling interview questions — what it is, why it hurts, and how to fix it with composition, Context, children props, and slot patterns.","react\u002Fstate-and-data-flow\u002Fprop-drilling-composition","Prop Drilling and Composition","lSE3CKtjSCZ0yfQ1sTV2AfbxI21sv2LXPhVBiKlk7jg",{"id":2710,"title":2711,"body":2712,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":2716,"navigation":100,"order":46,"path":2717,"questions":2718,"questionsCount":589,"related":169,"seo":2799,"seoDescription":2800,"stem":2801,"subtopic":2802,"topic":63,"topicSlug":65,"updated":420,"__hash__":2803},"qa\u002Freact\u002Fstate-management\u002Fasync-state-react-query.md","Async State React Query",{"type":91,"value":2713,"toc":2714},[],{"title":94,"searchDepth":11,"depth":11,"links":2715},[],{},"\u002Freact\u002Fstate-management\u002Fasync-state-react-query",[2719,2723,2727,2731,2735,2739,2743,2747,2751,2755,2759,2763,2767,2771,2775,2779,2783,2787,2791,2795],{"id":2720,"difficulty":96,"q":2721,"a":2722},"what-is-react-query","What is TanStack Query (React Query) and what problem does it solve?","**TanStack Query** (formerly React Query) is a library for managing\n**server state** in React apps. It solves the boilerplate problem of\nusing `useState` + `useEffect` to fetch data: no more manual loading\nflags, error states, cache invalidation, or race-condition handling.\n\n```jsx\n\u002F\u002F Without React Query — manual state machine\nconst [data, setData] = useState(null)\nconst [loading, setLoading] = useState(false)\nconst [error, setError] = useState(null)\n\nuseEffect(() => {\n  setLoading(true)\n  fetch('\u002Fapi\u002Fuser')\n    .then(r => r.json())\n    .then(d => { setData(d); setLoading(false) })\n    .catch(e => { setError(e); setLoading(false) })\n}, [])\n\n\u002F\u002F With React Query — three lines replace the whole block\nconst { data, isLoading, error } = useQuery({\n  queryKey: ['user'],\n  queryFn: () => fetch('\u002Fapi\u002Fuser').then(r => r.json()),\n})\n```\n\nReact Query also handles caching, background refetching, deduplication of\nconcurrent requests, and retry logic — all out of the box.\n\n**Rule of thumb:** if the data lives on a server and you want it fresh in\nthe UI, React Query is the right tool; it is not for ephemeral UI state\nlike modal toggles.\n",{"id":2724,"difficulty":96,"q":2725,"a":2726},"usequery-basics","What are the three required \u002F most-used options for useQuery?","`useQuery` needs at minimum a **queryKey** and a **queryFn**. The third\ncommon option is `staleTime`.\n\n```jsx\nimport { useQuery } from '@tanstack\u002Freact-query'\n\nfunction UserProfile({ userId }) {\n  const { data, isLoading, isError, error } = useQuery({\n    queryKey: ['user', userId],      \u002F\u002F cache key — array form\n    queryFn: () =>\n      fetch(`\u002Fapi\u002Fusers\u002F${userId}`).then(r => r.json()), \u002F\u002F async fetcher\n    staleTime: 1000 * 60 * 5,        \u002F\u002F treat data as fresh for 5 min\n  })\n\n  if (isLoading) return \u003CSpinner \u002F>\n  if (isError)   return \u003Cp>{error.message}\u003C\u002Fp>\n  return \u003Ch1>{data.name}\u003C\u002Fh1>\n}\n```\n\nThe hook returns many flags — `isPending`, `isFetching`, `isSuccess`,\n`isError`, `isStale` — plus `data`, `error`, and `refetch`. `isLoading`\nis true only on the very first fetch (no cached data + fetching); use\n`isFetching` to show a background-refetch spinner.\n\n**Rule of thumb:** always destructure only what you need; the full return\nobject has ~25 properties.\n",{"id":2728,"difficulty":96,"q":2729,"a":2730},"query-keys-importance","Why do query keys matter in React Query?","**Query keys** are the primary key for the query cache. Every unique key\nmaps to its own cache entry. React Query uses them to deduplicate\nin-flight requests, invalidate targeted entries after mutations, and\nre-run queries when the key changes.\n\n```jsx\n\u002F\u002F Different keys → different cache entries\nuseQuery({ queryKey: ['user', 1], queryFn: ... }) \u002F\u002F cache: user\u002F1\nuseQuery({ queryKey: ['user', 2], queryFn: ... }) \u002F\u002F cache: user\u002F2\n\n\u002F\u002F Key changes when userId changes → automatic re-fetch\nuseQuery({ queryKey: ['user', userId], queryFn: fetchUser })\n\n\u002F\u002F Invalidate all 'user' queries after an update\nqueryClient.invalidateQueries({ queryKey: ['user'] })\n```\n\nKeys are serialized deeply (arrays and objects both work), so\n`['users', { status: 'active' }]` and `['users', { status: 'inactive' }]`\nare independent cache slots. Treat keys like a URL: they should\nuniquely and fully describe the data being fetched.\n\n**Rule of thumb:** always include every variable the query depends on\n(IDs, filters, page numbers) in the key — never leave a dependency out.\n",{"id":2732,"difficulty":121,"q":2733,"a":2734},"stale-time-vs-cache-time","What is the difference between staleTime and gcTime (cacheTime)?","**`staleTime`** controls how long fetched data is considered fresh.\nDuring that window React Query will serve cached data without\nre-fetching. Default is `0` (immediately stale).\n\n**`gcTime`** (formerly `cacheTime`) controls how long **unused** cached\ndata is kept in memory before garbage collection. Default is 5 minutes.\n\n```jsx\nuseQuery({\n  queryKey: ['posts'],\n  queryFn: fetchPosts,\n  staleTime: 1000 * 60,      \u002F\u002F data is fresh for 1 min\n  gcTime: 1000 * 60 * 10,    \u002F\u002F cache kept 10 min after last observer\n})\n\u002F\u002F 0–60 s:  component mounts → serves cache, no network request\n\u002F\u002F 60+ s:   data is stale → re-fetch in background on next mount\u002Ffocus\n\u002F\u002F unmount: cache kept 10 min then collected\n```\n\nA high `staleTime` with a long `gcTime` means near-instant navigation\nbetween pages without spinners, while the data stays reasonably fresh.\n\n**Rule of thumb:** `staleTime` = \"how often can I tolerate stale data\";\n`gcTime` = \"how long should I cache data after nobody is watching it.\"\n",{"id":2736,"difficulty":121,"q":2737,"a":2738},"background-refetching","When does React Query automatically refetch data in the background?","React Query triggers a background refetch — silently updating stale data\nwithout showing a spinner — in four situations by default:\n\n```jsx\nuseQuery({\n  queryKey: ['todos'],\n  queryFn: fetchTodos,\n  \u002F\u002F all true by default:\n  refetchOnWindowFocus: true,     \u002F\u002F tab gets focus again\n  refetchOnMount: true,           \u002F\u002F new component instance mounts\n  refetchOnReconnect: true,       \u002F\u002F network comes back online\n  staleTime: 0,                   \u002F\u002F data is immediately stale\n})\n```\n\nDuring a background refetch `isFetching` is `true` but `isLoading`\nremains `false` (cached data still displayed). This lets you show a\nsubtle \"refreshing\" indicator without blanking the screen.\n\nYou can also trigger refetches manually via `refetch()` or via\n`queryClient.invalidateQueries({ queryKey: ['todos'] })`.\n\n**Rule of thumb:** increase `staleTime` to reduce unnecessary network\ntraffic; set `refetchOnWindowFocus: false` in forms or dashboards where\na mid-edit refresh would confuse users.\n",{"id":2740,"difficulty":96,"q":2741,"a":2742},"loading-error-success-states","How do you handle loading, error, and success states with useQuery?","`useQuery` returns boolean flags for every state in the request lifecycle.\nUse them to render the right UI slice.\n\n```jsx\nconst { data, isLoading, isError, error, isSuccess } = useQuery({\n  queryKey: ['profile'],\n  queryFn: fetchProfile,\n})\n\n\u002F\u002F isLoading: true only on first fetch (no cached data)\nif (isLoading) return \u003CSkeleton \u002F>\n\n\u002F\u002F isError: true when queryFn threw or rejected\nif (isError) return \u003CAlert message={error.message} \u002F>\n\n\u002F\u002F isSuccess: true once data is available (even stale)\nreturn \u003CProfile user={data} \u002F>\n```\n\nFor background refreshes where you want to keep showing old data while\nnew data loads, check `isFetching` alongside `isSuccess`.\n\n**Rule of thumb:** check `isLoading` for the empty-state skeleton and\n`isError` for the error boundary — never check `!data` as a loading\nproxy, because stale data can exist alongside a fresh fetch.\n",{"id":2744,"difficulty":121,"q":2745,"a":2746},"usemutation-basics","How does useMutation work and what callbacks does it provide?","**`useMutation`** handles write operations (POST, PUT, DELETE). Unlike\n`useQuery` it does not run automatically — you call `mutate()` or\n`mutateAsync()` explicitly.\n\n```jsx\nconst mutation = useMutation({\n  mutationFn: (newTodo) =>\n    fetch('\u002Fapi\u002Ftodos', {\n      method: 'POST',\n      body: JSON.stringify(newTodo),\n    }).then(r => r.json()),\n\n  onSuccess: (data, variables, context) => {\n    \u002F\u002F data = server response, variables = what you passed to mutate()\n    queryClient.invalidateQueries({ queryKey: ['todos'] })\n  },\n  onError: (error, variables, context) => {\n    console.error('Mutation failed:', error.message)\n  },\n  onSettled: () => {\n    \u002F\u002F runs after onSuccess OR onError — good for cleanup\n  },\n})\n\n\u002F\u002F trigger it\nmutation.mutate({ title: 'Buy milk' })\n```\n\n`mutation.status` cycles through `idle → pending → success | error`.\nUse `mutation.isPending` to disable the submit button.\n\n**Rule of thumb:** always invalidate or update the relevant query cache\nin `onSuccess` so the UI stays in sync with the server.\n",{"id":2748,"difficulty":208,"q":2749,"a":2750},"optimistic-updates","How do you implement optimistic updates with useMutation?","**Optimistic updates** apply the expected change to the cache immediately\n— before the server responds — so the UI feels instant. If the mutation\nfails you roll back using the snapshot saved in `onMutate`.\n\n```jsx\nconst queryClient = useQueryClient()\n\nconst toggleTodo = useMutation({\n  mutationFn: (todo) =>\n    fetch(`\u002Fapi\u002Ftodos\u002F${todo.id}`, { method: 'PATCH',\n      body: JSON.stringify({ done: !todo.done }) }).then(r => r.json()),\n\n  onMutate: async (todo) => {\n    \u002F\u002F 1. cancel any in-flight refetch (avoids overwriting our optimistic data)\n    await queryClient.cancelQueries({ queryKey: ['todos'] })\n\n    \u002F\u002F 2. snapshot the previous value\n    const previous = queryClient.getQueryData(['todos'])\n\n    \u002F\u002F 3. optimistically update the cache\n    queryClient.setQueryData(['todos'], (old) =>\n      old.map(t => t.id === todo.id ? { ...t, done: !t.done } : t)\n    )\n\n    return { previous }  \u002F\u002F returned as \"context\"\n  },\n\n  onError: (_err, _todo, context) => {\n    \u002F\u002F 4. roll back on failure\n    queryClient.setQueryData(['todos'], context.previous)\n  },\n\n  onSettled: () => {\n    \u002F\u002F 5. always refetch to sync with server truth\n    queryClient.invalidateQueries({ queryKey: ['todos'] })\n  },\n})\n```\n\nThe pattern — cancel → snapshot → optimistic write → rollback on error →\ninvalidate on settle — is the canonical React Query optimistic workflow.\n\n**Rule of thumb:** use optimistic updates for high-frequency interactions\n(toggles, likes, reorder) where a 200–500 ms spinner would feel sluggish.\n",{"id":2752,"difficulty":121,"q":2753,"a":2754},"query-invalidation","How does query invalidation work and why is it preferred over manual cache writes?","**Query invalidation** marks a cached query as stale so that React Query\nrefetches it the next time an active observer is present (or immediately\nif one is currently mounted).\n\n```jsx\n\u002F\u002F After creating a new post, invalidate the list\nconst mutation = useMutation({\n  mutationFn: createPost,\n  onSuccess: () => {\n    \u002F\u002F exact: false (default) — invalidates 'posts' and all sub-keys\n    queryClient.invalidateQueries({ queryKey: ['posts'] })\n\n    \u002F\u002F exact: true — invalidates only this exact key\n    queryClient.invalidateQueries({ queryKey: ['posts', 'list'], exact: true })\n  },\n})\n```\n\nInvalidation is preferred over `setQueryData` for list-after-create\nbecause the server may add timestamps, IDs, or derived fields that the\nclient can't predict. Re-fetching gets you the canonical truth.\n\n**Rule of thumb:** invalidate for create\u002Fdelete operations (server adds\ndata you don't have); use `setQueryData` only for update operations where\nyou already have the complete new object.\n",{"id":2756,"difficulty":121,"q":2757,"a":2758},"dependent-queries","How do you fetch query B only after query A has completed (dependent queries)?","Use the **`enabled`** option. When `enabled` is `false` the query is\nsuspended — it will not fetch until the expression becomes truthy.\n\n```jsx\nfunction UserOrders({ userId }) {\n  \u002F\u002F Step 1: fetch the user\n  const { data: user } = useQuery({\n    queryKey: ['user', userId],\n    queryFn: () => fetchUser(userId),\n  })\n\n  \u002F\u002F Step 2: fetch orders only once we have user.accountId\n  const { data: orders } = useQuery({\n    queryKey: ['orders', user?.accountId],\n    queryFn: () => fetchOrders(user.accountId),\n    enabled: !!user?.accountId,  \u002F\u002F waits for accountId to exist\n  })\n\n  return \u003COrderList orders={orders} \u002F>\n}\n```\n\n`enabled` accepts any expression — `!!id`, `isSuccess`, a feature flag\n— and React Query will start the query the moment the value becomes\ntruthy.\n\n**Rule of thumb:** always guard with `!!value` not just `value`, so that\nempty strings and `0` don't accidentally enable a query.\n",{"id":2760,"difficulty":208,"q":2761,"a":2762},"infinite-queries","How does useInfiniteQuery work for paginated or infinite-scroll data?","**`useInfiniteQuery`** manages a list of pages. Each page is fetched via\n`queryFn` which receives `pageParam` (the cursor\u002Fpage number for that\npage). `getNextPageParam` derives the next cursor from the last page.\n\n```jsx\nconst {\n  data,           \u002F\u002F { pages: [...], pageParams: [...] }\n  fetchNextPage,\n  hasNextPage,\n  isFetchingNextPage,\n} = useInfiniteQuery({\n  queryKey: ['posts'],\n  queryFn: ({ pageParam = 0 }) =>\n    fetch(`\u002Fapi\u002Fposts?cursor=${pageParam}`).then(r => r.json()),\n\n  getNextPageParam: (lastPage) =>\n    lastPage.nextCursor ?? undefined, \u002F\u002F undefined stops pagination\n\n  initialPageParam: 0,               \u002F\u002F v5 required option\n})\n\n\u002F\u002F Flatten all pages for rendering\nconst posts = data?.pages.flatMap(p => p.items) ?? []\n```\n\nCall `fetchNextPage()` from an \"Load more\" button or an intersection\nobserver at the bottom of the list.\n\n**Rule of thumb:** `useInfiniteQuery` is for cursor-based or\noffset-based lists; for discrete page navigation (page 1 \u002F 2 \u002F 3\nbuttons) use a plain `useQuery` with the page number in the key.\n",{"id":2764,"difficulty":96,"q":2765,"a":2766},"queryclient-setup","How do you set up QueryClient and QueryClientProvider?","Create a **`QueryClient`** instance once at app startup and wrap the\ntree with **`QueryClientProvider`**. Every hook inside the tree shares\nthat client.\n\n```jsx\n\u002F\u002F main.tsx \u002F _app.tsx\nimport { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query'\nimport { ReactQueryDevtools } from '@tanstack\u002Freact-query-devtools'\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 1000 * 60,      \u002F\u002F global 1-min freshness\n      retry: 2,                   \u002F\u002F retry failed queries twice\n    },\n  },\n})\n\nexport default function App() {\n  return (\n    \u003CQueryClientProvider client={queryClient}>\n      \u003CRouter \u002F>\n      \u003CReactQueryDevtools initialIsOpen={false} \u002F> {\u002F* dev-only panel *\u002F}\n    \u003C\u002FQueryClientProvider>\n  )\n}\n```\n\n`QueryClient` is created outside the component so it is not re-created\non every render. In tests, create a fresh `QueryClient` per test to\nprevent shared state between cases.\n\n**Rule of thumb:** never create the `QueryClient` inside a component —\nit would reset the cache on every render.\n",{"id":2768,"difficulty":121,"q":2769,"a":2770},"prefetching","What is prefetchQuery and when would you use it?","**`prefetchQuery`** populates the cache proactively — before the\ncomponent that needs the data has mounted. This eliminates the loading\nstate for routes the user is likely to visit next.\n\n```jsx\nconst queryClient = useQueryClient()\n\n\u002F\u002F On mouse-enter of a \"User Profile\" link\nfunction UserLink({ userId }) {\n  const handleMouseEnter = () => {\n    queryClient.prefetchQuery({\n      queryKey: ['user', userId],\n      queryFn: () => fetchUser(userId),\n      staleTime: 1000 * 60 * 5,  \u002F\u002F don't prefetch if cached and fresh\n    })\n  }\n  return \u003CLink to={`\u002Fusers\u002F${userId}`} onMouseEnter={handleMouseEnter}>...\u003C\u002FLink>\n}\n```\n\nYou can also call `prefetchQuery` in a route loader (React Router v6+,\nNext.js `getServerSideProps`, TanStack Router) to fetch data on the\nserver before the page renders.\n\n**Rule of thumb:** prefetch on hover\u002Fintent; do not prefetch every\npossible route eagerly or you'll waste bandwidth.\n",{"id":2772,"difficulty":121,"q":2773,"a":2774},"server-vs-client-state","What is the difference between server state and client state, and why does it matter?","**Server state** is data that lives on a remote server and is fetched\nasynchronously — it can change outside your app (another user edits it,\na cron job updates it). **Client state** is ephemeral UI state that only\nyour app owns: modal open\u002Fclosed, form draft, selected tab.\n\n```jsx\n\u002F\u002F Client state — lives only in the browser\nconst [isModalOpen, setIsModalOpen] = useState(false)\n\n\u002F\u002F Server state — a snapshot of remote data\nconst { data: todos } = useQuery({\n  queryKey: ['todos'],\n  queryFn: fetchTodos,\n})\n\u002F\u002F \"todos\" can become stale while the user is on the page\n```\n\nServer state has extra concerns client state doesn't: caching, staleness,\nbackground synchronisation, deduplication, pagination. Managing it with\n`useState` forces you to re-implement all of these manually.\n\n**Rule of thumb:** use `useState`\u002F`useReducer`\u002FZustand for client state;\nuse React Query (or SWR) for server state — keep the two layers separate.\n",{"id":2776,"difficulty":121,"q":2777,"a":2778},"pagination-patterns","How do you implement paginated data fetching with useQuery?","Include the **page number** in the query key so each page has its own\ncache entry. Use **`placeholderData: keepPreviousData`** (v5) to avoid\nblanking the UI between pages.\n\n```jsx\nimport { useQuery, keepPreviousData } from '@tanstack\u002Freact-query'\n\nfunction TodoList() {\n  const [page, setPage] = useState(1)\n\n  const { data, isPlaceholderData } = useQuery({\n    queryKey: ['todos', page],\n    queryFn: () => fetchTodos(page),\n    placeholderData: keepPreviousData, \u002F\u002F keep showing page N while N+1 loads\n  })\n\n  return (\n    \u003C>\n      {isPlaceholderData && \u003CLoadingBar \u002F>}\n      \u003CList items={data?.items} \u002F>\n      \u003Cbutton\n        onClick={() => setPage(p => p - 1)}\n        disabled={page === 1}>Prev\u003C\u002Fbutton>\n      \u003Cbutton\n        onClick={() => setPage(p => p + 1)}\n        disabled={!data?.hasMore}>Next\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\nReact Query can also prefetch the next page when `hasMore` is true,\nmaking forward navigation feel instant.\n\n**Rule of thumb:** always put pagination state in the query key; never\nmutate an external variable that the key doesn't reflect.\n",{"id":2780,"difficulty":121,"q":2781,"a":2782},"react-query-vs-redux","When should you use React Query instead of Redux for data fetching?","Use **React Query** when data originates from a server and your main\nconcerns are caching, staleness, and background sync. Use **Redux** when\nyou have complex cross-cutting client state (undo\u002Fredo, multi-step\nworkflows, real-time collaborative state shared between many components).\n\n```jsx\n\u002F\u002F React Query — server data, auto-caching, zero boilerplate\nconst { data: user } = useQuery({ queryKey: ['user'], queryFn: getUser })\n\n\u002F\u002F Redux (RTK Query) — same fetching capability, but adds global client state\nconst user = useSelector(selectUser)       \u002F\u002F cross-slice derivations\ndispatch(userSlice.actions.setRole('admin')) \u002F\u002F synchronous client mutation\n```\n\nMany teams use both: React Query for server state, Zustand or Redux for\nthe small amount of true client state (auth session, UI preferences).\nRTK Query (Redux Toolkit's built-in fetching solution) is an alternative\nif you're already invested in Redux.\n\n**Rule of thumb:** reach for React Query first; only add Redux if you\ngenuinely need global synchronous state across many parts of the app.\n",{"id":2784,"difficulty":121,"q":2785,"a":2786},"react-query-vs-swr","How does React Query compare to SWR?","Both **SWR** (Vercel) and **React Query** implement stale-while-revalidate\ncaching. The differences are in scope and power:\n\n```jsx\n\u002F\u002F SWR — minimal, opinionated API\nconst { data, error, isLoading } = useSWR('\u002Fapi\u002Fuser', fetcher)\n\n\u002F\u002F React Query — richer API: mutations, infinite queries, optimistic updates\nconst { data, isLoading } = useQuery({ queryKey: ['\u002Fapi\u002Fuser'], queryFn: fetcher })\nconst mutation = useMutation({ mutationFn: updateUser })\n```\n\nSWR is simpler — almost no configuration, tiny bundle. React Query has\nmore features: `useMutation` with full lifecycle callbacks, `useInfiniteQuery`,\n`QueryClient` for server-side prefetching, advanced `select` transforms,\nand the excellent DevTools panel.\n\n**Rule of thumb:** SWR for small apps that only need fetch-and-cache;\nReact Query when you also need mutations, optimistic updates, pagination,\nor fine-grained cache control.\n",{"id":2788,"difficulty":208,"q":2789,"a":2790},"suspense-mode","How does React Query integrate with React Suspense?","Pass **`useSuspenseQuery`** (v5) instead of `useQuery` to let React\nSuspense handle the loading state declaratively. The component suspends\nwhile data loads; wrap it in `\u003CSuspense>` with a fallback.\n\n```jsx\n\u002F\u002F v5 API\nimport { useSuspenseQuery } from '@tanstack\u002Freact-query'\n\nfunction UserProfile({ userId }) {\n  \u002F\u002F throws a Promise while loading → Suspense catches it\n  const { data } = useSuspenseQuery({\n    queryKey: ['user', userId],\n    queryFn: () => fetchUser(userId),\n  })\n  \u002F\u002F data is ALWAYS defined here — no isLoading check needed\n  return \u003Ch1>{data.name}\u003C\u002Fh1>\n}\n\n\u002F\u002F Parent\nfunction Page() {\n  return (\n    \u003CErrorBoundary fallback={\u003CErrorPage \u002F>}>\n      \u003CSuspense fallback={\u003CSkeleton \u002F>}>\n        \u003CUserProfile userId={1} \u002F>\n      \u003C\u002FSuspense>\n    \u003C\u002FErrorBoundary>\n  )\n}\n```\n\nError states bubble to the nearest `\u003CErrorBoundary>` instead of being\nreturned as a flag. This moves loading and error handling out of the\ndata component and into the layout layer.\n\n**Rule of thumb:** use Suspense mode when you want co-located data\nfetching with parent-controlled loading UI; keep the imperative\n`isLoading` approach in forms and components that handle their own\nerror display.\n",{"id":2792,"difficulty":208,"q":2793,"a":2794},"ssr-hydration","How do you use React Query with SSR (e.g., Next.js) and avoid hydration mismatches?","Use **`HydrationBoundary`** with `dehydrate` to pass the server-fetched\ncache to the client, avoiding a client-side refetch on first render.\n\n```jsx\n\u002F\u002F app\u002Fusers\u002Fpage.tsx (Next.js App Router)\nimport { dehydrate, HydrationBoundary, QueryClient }\n  from '@tanstack\u002Freact-query'\n\nexport default async function UsersPage() {\n  const queryClient = new QueryClient()\n\n  \u002F\u002F prefetch on the server\n  await queryClient.prefetchQuery({\n    queryKey: ['users'],\n    queryFn: fetchUsers,\n  })\n\n  return (\n    \u002F\u002F serialise the cache and send it to the client\n    \u003CHydrationBoundary state={dehydrate(queryClient)}>\n      \u003CUserList \u002F>   {\u002F* useQuery(['users']) hits the cache immediately *\u002F}\n    \u003C\u002FHydrationBoundary>\n  )\n}\n```\n\nOn the client, React Query reads the dehydrated state and populates its\ncache before components mount, so they render with data immediately and\nno hydration mismatch occurs.\n\n**Rule of thumb:** always create a fresh `QueryClient` per request on the\nserver — never share the same instance across requests or you'll leak data\nbetween users.\n",{"id":2796,"difficulty":121,"q":2797,"a":2798},"polling-refetch-interval","How do you poll an endpoint on a fixed interval with React Query?","Set **`refetchInterval`** to a millisecond value. React Query will refetch\nthe query on that interval while the component is mounted. Set\n`refetchIntervalInBackground: true` to keep polling even when the tab is\nnot focused.\n\n```jsx\nconst { data: jobStatus } = useQuery({\n  queryKey: ['job', jobId],\n  queryFn: () => fetchJobStatus(jobId),\n  refetchInterval: 3000,               \u002F\u002F poll every 3 seconds\n  refetchIntervalInBackground: false,  \u002F\u002F pause when tab is hidden (default)\n})\n\n\u002F\u002F Dynamic interval — stop polling once the job is done\nconst { data } = useQuery({\n  queryKey: ['job', jobId],\n  queryFn: () => fetchJobStatus(jobId),\n  refetchInterval: (query) => {\n    \u002F\u002F return false to stop; return ms to continue\n    return query.state.data?.status === 'done' ? false : 2000\n  },\n})\n```\n\nThe function form of `refetchInterval` receives the current query object,\nletting you implement \"poll until done\" patterns without extra state.\n\n**Rule of thumb:** always use the function form when you want to stop\npolling on a condition — a fixed interval that never stops wastes network\non completed resources.\n",{"description":94},"React Query interview questions — useQuery, useMutation, query keys, caching, stale time, refetching, optimistic updates, and async state management patterns.","react\u002Fstate-management\u002Fasync-state-react-query","Async State & React Query","iVoG3RXlvfrXcn1S9vuDDXabHtP3hZ0NtPwLeckGYTA",{"id":2805,"title":2806,"body":2807,"description":94,"difficulty":208,"extension":97,"framework":10,"frameworkSlug":8,"meta":2811,"navigation":100,"order":46,"path":2812,"questions":2813,"questionsCount":2214,"related":169,"seo":2866,"seoDescription":2867,"stem":2868,"subtopic":2806,"topic":81,"topicSlug":83,"updated":420,"__hash__":2869},"qa\u002Freact\u002Ftesting\u002Ftesting-custom-hooks.md","Testing Custom Hooks",{"type":91,"value":2808,"toc":2809},[],{"title":94,"searchDepth":11,"depth":11,"links":2810},[],{},"\u002Freact\u002Ftesting\u002Ftesting-custom-hooks",[2814,2818,2822,2826,2830,2834,2838,2842,2846,2850,2854,2858,2862],{"id":2815,"difficulty":121,"q":2816,"a":2817},"render-hook-intro","What is `renderHook` and how does it work?","`renderHook` from `@testing-library\u002Freact` mounts a minimal wrapper\ncomponent that calls the hook under test and exposes its return value\nthrough a `result` object. It is the standard way to test hooks in\nisolation without building a throwaway UI component.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { useCounter } from '.\u002FuseCounter'\n\ntest('initializes with given value', () => {\n  const { result } = renderHook(() => useCounter(10))\n\n  \u002F\u002F result.current holds whatever the hook returns\n  expect(result.current.count).toBe(10)\n  expect(typeof result.current.increment).toBe('function')\n})\n```\n\nThe wrapper component re-renders each time the hook causes a state\nupdate, and `result.current` always reflects the *latest* return value.\n\nInternally, `renderHook(callback)` is roughly:\n```js\nfunction TestHook() {\n  result.current = callback()  \u002F\u002F captures hook return value\n  return null\n}\nrender(\u003CTestHook \u002F>)\n```\n\nUpdating arguments on re-render:\n```js\nconst { result, rerender } = renderHook(\n  ({ step }) => useCounter(0, step),\n  { initialProps: { step: 1 } }\n)\nrerender({ step: 5 })   \u002F\u002F hook re-executes with new props\n```\n\n**Rule of thumb:** Use `renderHook` when the hook encapsulates logic\nyou want to verify independently; use component tests when the\nhook is simple and fully covered by the component's test.\n",{"id":2819,"difficulty":121,"q":2820,"a":2821},"act-in-hook-tests","Why do you need to wrap hook state updates in `act()`?","React batches state updates and applies them asynchronously in its\nfiber scheduler. `act()` tells React's test utilities: \"all state changes\ninside this block should be flushed before assertions run.\" Without it\nyou may assert before React has committed the updated state.\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { useToggle } from '.\u002FuseToggle'\n\ntest('toggles between true and false', () => {\n  const { result } = renderHook(() => useToggle(false))\n\n  expect(result.current.isOn).toBe(false)\n\n  \u002F\u002F Wrap the state-updating call in act()\n  act(() => {\n    result.current.toggle()\n  })\n\n  expect(result.current.isOn).toBe(true)\n\n  act(() => {\n    result.current.toggle()\n  })\n\n  expect(result.current.isOn).toBe(false)\n})\n```\n\nFor async updates (hooks that call `setState` inside a Promise or timer):\n```js\nawait act(async () => {\n  result.current.fetchData()\n})\n\u002F\u002F Safe to assert here — all async state changes have been flushed\n```\n\nRTL's `userEvent` helpers wrap their actions in `act` automatically, which\nis one reason to prefer `userEvent` over manual `act` calls in component\ntests. For hook tests you usually call `act` directly.\n\n**Rule of thumb:** Any call inside `renderHook` that triggers a state\nupdate must be wrapped in `act()`. If you see the \"not wrapped in act\"\nwarning, find the state update and wrap the trigger.\n",{"id":2823,"difficulty":208,"q":2824,"a":2825},"async-hook-test","How do you test a custom hook that fetches data asynchronously?","Combine `renderHook`, `waitFor`, and a mock (MSW or `vi.fn()`) to assert\non loading, success, and error states.\n\n```js\nimport { renderHook, waitFor } from '@testing-library\u002Freact'\nimport { server } from '..\u002Fmocks\u002Fserver'\nimport { http, HttpResponse } from 'msw'\nimport { useFetchUser } from '.\u002FuseFetchUser'\n\n\u002F\u002F useFetchUser: returns { user, loading, error }\n\ntest('transitions loading → data', async () => {\n  server.use(\n    http.get('\u002Fapi\u002Fusers\u002F1', () =>\n      HttpResponse.json({ id: 1, name: 'Alice' })\n    )\n  )\n\n  const { result } = renderHook(() => useFetchUser(1))\n\n  \u002F\u002F Loading state immediately after mount\n  expect(result.current.loading).toBe(true)\n  expect(result.current.user).toBeNull()\n\n  \u002F\u002F Wait until loading is false\n  await waitFor(() => expect(result.current.loading).toBe(false))\n\n  expect(result.current.user).toEqual({ id: 1, name: 'Alice' })\n  expect(result.current.error).toBeNull()\n})\n\ntest('sets error on fetch failure', async () => {\n  server.use(\n    http.get('\u002Fapi\u002Fusers\u002F1', () => new HttpResponse(null, { status: 500 }))\n  )\n\n  const { result } = renderHook(() => useFetchUser(1))\n\n  await waitFor(() => expect(result.current.loading).toBe(false))\n\n  expect(result.current.error).toBeTruthy()\n  expect(result.current.user).toBeNull()\n})\n```\n\n**Rule of thumb:** Use `waitFor(() => expect(...))` rather than\n`findBy*` in hook tests because hooks return values, not DOM elements.\n",{"id":2827,"difficulty":121,"q":2828,"a":2829},"context-hook-test","How do you test a hook that depends on React context?","Pass a `wrapper` option to `renderHook` that wraps the hook in the\nrequired provider, the same way you'd wrap a component.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { AuthProvider } from '.\u002FAuthContext'\nimport { useAuth } from '.\u002FuseAuth'\n\nfunction createWrapper(user) {\n  return function AuthWrapper({ children }) {\n    return \u003CAuthProvider initialUser={user}>{children}\u003C\u002FAuthProvider>\n  }\n}\n\ntest('returns current user from context', () => {\n  const { result } = renderHook(() => useAuth(), {\n    wrapper: createWrapper({ id: 1, name: 'Alice', role: 'admin' }),\n  })\n\n  expect(result.current.user).toEqual({ id: 1, name: 'Alice', role: 'admin' })\n  expect(result.current.isAuthenticated).toBe(true)\n})\n\ntest('returns null user when not authenticated', () => {\n  const { result } = renderHook(() => useAuth(), {\n    wrapper: createWrapper(null),\n  })\n\n  expect(result.current.user).toBeNull()\n  expect(result.current.isAuthenticated).toBe(false)\n})\n\ntest('throws when used outside AuthProvider', () => {\n  \u002F\u002F No wrapper — hook should throw\n  expect(() => renderHook(() => useAuth())).toThrow(\u002Fmust be used within AuthProvider\u002Fi)\n})\n```\n\n**Rule of thumb:** Always test the \"missing provider\" error path for\nhooks that call `useContext` — it verifies your guard condition and\nproduces a clear error message in production.\n",{"id":2831,"difficulty":121,"q":2832,"a":2833},"useeffect-hook-test","How do you test the side effects of a custom hook that uses `useEffect`?","Wrap the side-effectful action in `act()` and assert the result after\n`act` resolves. For side effects that happen on mount, they run\nsynchronously during `renderHook` (within RTL's own `act` call).\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { useDocumentTitle } from '.\u002FuseDocumentTitle'\n\n\u002F\u002F useDocumentTitle: sets document.title to the given string\ntest('sets document title on mount', () => {\n  renderHook(() => useDocumentTitle('My Page'))\n  expect(document.title).toBe('My Page')\n})\n\ntest('updates document title when value changes', () => {\n  const { rerender } = renderHook(\n    ({ title }) => useDocumentTitle(title),\n    { initialProps: { title: 'Page 1' } }\n  )\n\n  expect(document.title).toBe('Page 1')\n\n  rerender({ title: 'Page 2' })\n  expect(document.title).toBe('Page 2')\n})\n\ntest('restores title on unmount', () => {\n  document.title = 'Original'\n  const { unmount } = renderHook(() => useDocumentTitle('Temp'))\n  expect(document.title).toBe('Temp')\n  unmount()\n  expect(document.title).toBe('Original')\n})\n```\n\nFor async effects (data fetching on mount), use `waitFor` as shown in\nthe `useFetchUser` example.\n\n**Rule of thumb:** Test three lifecycle moments: initial mount effect,\nupdate-triggered effect, and cleanup on unmount.\n",{"id":2835,"difficulty":208,"q":2836,"a":2837},"timer-hook-test","How do you test a custom hook that internally uses `setTimeout` or `setInterval`?","Use `vi.useFakeTimers()` and advance time inside `act()`.\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport { useDebounce } from '.\u002FuseDebounce'\n\ndescribe('useDebounce', () => {\n  beforeEach(() => vi.useFakeTimers())\n  afterEach(() => vi.useRealTimers())\n\n  test('returns initial value immediately', () => {\n    const { result } = renderHook(() => useDebounce('hello', 300))\n    expect(result.current).toBe('hello')\n  })\n\n  test('does not update before delay', () => {\n    const { result, rerender } = renderHook(\n      ({ value }) => useDebounce(value, 300),\n      { initialProps: { value: 'hello' } }\n    )\n\n    rerender({ value: 'world' })\n    act(() => vi.advanceTimersByTime(100))\n    expect(result.current).toBe('hello')   \u002F\u002F not yet updated\n  })\n\n  test('updates after debounce delay', () => {\n    const { result, rerender } = renderHook(\n      ({ value }) => useDebounce(value, 300),\n      { initialProps: { value: 'hello' } }\n    )\n\n    rerender({ value: 'world' })\n    act(() => vi.advanceTimersByTime(300))\n    expect(result.current).toBe('world')\n  })\n\n  test('clears pending timer on unmount', () => {\n    const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout')\n    const { unmount } = renderHook(() => useDebounce('test', 300))\n    unmount()\n    expect(clearTimeoutSpy).toHaveBeenCalled()\n  })\n})\n```\n\n**Rule of thumb:** Always test the cleanup path (`unmount`) for timer\nhooks — leaking timers is a common source of \"act warning\" failures in\nsubsequent tests.\n",{"id":2839,"difficulty":121,"q":2840,"a":2841},"reducer-hook-test","How do you test a hook built on `useReducer`?","Test through the hook's public interface (the actions\u002Fdispatch wrappers\nit returns), not by inspecting the reducer directly. The reducer itself\ncan have its own pure unit tests.\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { useShoppingCart } from '.\u002FuseShoppingCart'\n\ntest('adds and removes items', () => {\n  const { result } = renderHook(() => useShoppingCart())\n\n  expect(result.current.items).toHaveLength(0)\n  expect(result.current.total).toBe(0)\n\n  act(() => {\n    result.current.addItem({ id: 1, name: 'Widget', price: 9.99 })\n  })\n  expect(result.current.items).toHaveLength(1)\n  expect(result.current.total).toBeCloseTo(9.99)\n\n  act(() => {\n    result.current.addItem({ id: 2, name: 'Gadget', price: 24.99 })\n  })\n  expect(result.current.total).toBeCloseTo(34.98)\n\n  act(() => {\n    result.current.removeItem(1)\n  })\n  expect(result.current.items).toHaveLength(1)\n  expect(result.current.total).toBeCloseTo(24.99)\n})\n\ntest('clears cart', () => {\n  const { result } = renderHook(() => useShoppingCart())\n  act(() => {\n    result.current.addItem({ id: 1, name: 'Widget', price: 9.99 })\n    result.current.clearCart()\n  })\n  expect(result.current.items).toHaveLength(0)\n})\n```\n\nSeparately, test the reducer as a pure function:\n```js\nimport { cartReducer } from '.\u002FcartReducer'\n\ntest('ADD_ITEM increases quantity for existing item', () => {\n  const state = { items: [{ id: 1, qty: 1, price: 10 }] }\n  const next = cartReducer(state, { type: 'ADD_ITEM', payload: { id: 1 } })\n  expect(next.items[0].qty).toBe(2)\n})\n```\n\n**Rule of thumb:** Test hooks through their action API (what the caller\nsees); test reducers as pure functions (same input → same output).\n",{"id":2843,"difficulty":96,"q":2844,"a":2845},"hook-args-test","How do you test a hook that accepts arguments, and test how it behaves when those arguments change?","Pass `initialProps` to `renderHook` and call `rerender` with new props\nto simulate prop changes.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { usePagination } from '.\u002FusePagination'\n\ntest('initializes with given page and pageSize', () => {\n  const { result } = renderHook(\n    ({ page, pageSize }) => usePagination({ page, pageSize, total: 100 }),\n    { initialProps: { page: 1, pageSize: 10 } }\n  )\n\n  expect(result.current.currentPage).toBe(1)\n  expect(result.current.totalPages).toBe(10)\n  expect(result.current.startIndex).toBe(0)\n  expect(result.current.endIndex).toBe(9)\n})\n\ntest('recalculates when pageSize changes', () => {\n  const { result, rerender } = renderHook(\n    ({ pageSize }) => usePagination({ page: 1, pageSize, total: 100 }),\n    { initialProps: { pageSize: 10 } }\n  )\n\n  expect(result.current.totalPages).toBe(10)\n\n  rerender({ pageSize: 20 })\n  expect(result.current.totalPages).toBe(5)\n})\n\ntest('clamps page when it exceeds new totalPages', () => {\n  const { result, rerender } = renderHook(\n    ({ total }) => usePagination({ page: 5, pageSize: 10, total }),\n    { initialProps: { total: 100 } }\n  )\n\n  expect(result.current.currentPage).toBe(5)\n\n  rerender({ total: 30 })    \u002F\u002F now only 3 pages\n  expect(result.current.currentPage).toBe(3)  \u002F\u002F clamped\n})\n```\n\n**Rule of thumb:** Use `initialProps` + `rerender` to test argument\nchanges; this is the hook equivalent of testing prop changes via\n`render` + `rerender`.\n",{"id":2847,"difficulty":121,"q":2848,"a":2849},"hook-vs-component-test","When should you test a custom hook directly with `renderHook` versus testing it through the component that uses it?","Both are valid — the choice depends on the complexity and reuse of the\nhook.\n\n**Test through the component when:**\n- The hook is tightly coupled to one component and used nowhere else.\n- The hook's behavior is fully visible through the component's UI.\n- The hook is simple (one or two state variables).\n\n```js\n\u002F\u002F useExpanded is used only in Accordion — test through Accordion\ntest('expands panel on click', async () => {\n  const user = userEvent.setup()\n  render(\u003CAccordion panels={[{ title: 'Q1', body: 'A1' }]} \u002F>)\n  expect(screen.queryByText('A1')).not.toBeInTheDocument()\n  await user.click(screen.getByRole('button', { name: \u002FQ1\u002Fi }))\n  expect(screen.getByText('A1')).toBeInTheDocument()\n})\n```\n\n**Test the hook directly when:**\n- The hook encapsulates complex logic used by multiple components.\n- The hook has many edge cases that would be tedious to exercise via UI.\n- The hook manages async state (loading\u002Ferror\u002Fdata transitions).\n- You want to document the hook's API contract separately.\n\n```js\n\u002F\u002F useInfiniteScroll is reused across 4 components — test directly\ntest('loads next page when sentinel is visible', async () => {\n  const { result } = renderHook(() => useInfiniteScroll(fetchPage))\n  \u002F\u002F ... assert loading, page count, hasMore\n})\n```\n\nA practical middle ground: write one integration test per major use-case\nat the component level, then add direct hook tests for tricky edge cases.\n\n**Rule of thumb:** Start with component tests. Add direct hook tests\nwhen the component test becomes unwieldy or when the hook is shared.\n",{"id":2851,"difficulty":208,"q":2852,"a":2853},"mock-hook-dependency","How do you mock a module dependency used inside a custom hook?","Use `vi.mock()` to replace the dependency before the hook (and any\ncomponent using it) imports it.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport * as analyticsModule from '..\u002Futils\u002Fanalytics'\nimport { useProductView } from '.\u002FuseProductView'\n\nvi.mock('..\u002Futils\u002Fanalytics')\n\nconst mockedAnalytics = vi.mocked(analyticsModule)\n\nbeforeEach(() => {\n  mockedAnalytics.trackEvent.mockImplementation(() => {})\n})\n\nafterEach(() => vi.clearAllMocks())\n\ntest('tracks product view event on mount', () => {\n  renderHook(() => useProductView({ id: 42, name: 'Widget' }))\n\n  expect(mockedAnalytics.trackEvent).toHaveBeenCalledWith('product_view', {\n    product_id: 42,\n    product_name: 'Widget',\n  })\n})\n\ntest('tracks only once even if hook re-renders', () => {\n  const { rerender } = renderHook(() => useProductView({ id: 42, name: 'Widget' }))\n  rerender()\n  rerender()\n  expect(mockedAnalytics.trackEvent).toHaveBeenCalledTimes(1)\n})\n```\n\nFor mocking a hook that's used inside another hook (hook composition):\n```js\nvi.mock('.\u002FuseAuth', () => ({\n  useAuth: () => ({ user: { id: 1 }, isAuthenticated: true }),\n}))\n```\n\n**Rule of thumb:** Mock at the module boundary, not deep inside the\nimplementation. If you're mocking an internal helper, the hook's\nresponsibility boundary may be too large.\n",{"id":2855,"difficulty":208,"q":2856,"a":2857},"ref-hook-test","How do you test a custom hook that uses `useRef` to interact with DOM elements?","`renderHook` by itself doesn't produce DOM elements — you need a component\nto attach refs to real DOM nodes. Use a custom wrapper component.\n\n```js\nimport { render, screen, act } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { useAutoFocus } from '.\u002FuseAutoFocus'\n\n\u002F\u002F useAutoFocus: takes a ref and calls .focus() on mount\n\ntest('focuses element on mount', () => {\n  function TestComponent() {\n    const ref = React.useRef(null)\n    useAutoFocus(ref)\n    return \u003Cinput ref={ref} aria-label=\"Auto-focused input\" \u002F>\n  }\n\n  render(\u003CTestComponent \u002F>)\n  expect(screen.getByRole('textbox', { name: \u002Fauto-focused\u002Fi })).toHaveFocus()\n})\n```\n\nFor hooks that expose a ref (e.g., `useScrollLock`):\n```js\nfunction TestComponent() {\n  const containerRef = React.useRef(null)\n  const { lock, unlock } = useScrollLock(containerRef)\n  return (\n    \u003Cdiv ref={containerRef} style={{ overflow: 'auto' }}>\n      \u003Cbutton onClick={lock}>Lock\u003C\u002Fbutton>\n      \u003Cbutton onClick={unlock}>Unlock\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n\ntest('sets overflow hidden on lock', async () => {\n  const user = userEvent.setup()\n  render(\u003CTestComponent \u002F>)\n  await user.click(screen.getByRole('button', { name: \u002Flock\u002Fi }))\n  \u002F\u002F Assert DOM side-effect\n  expect(screen.getByRole('button', { name: \u002Flock\u002Fi }).closest('div'))\n    .toHaveStyle({ overflow: 'hidden' })\n})\n```\n\n**Rule of thumb:** When a hook needs a real DOM node (for refs,\nresize observers, intersection observers), write a small wrapper\ncomponent in the test file rather than forcing `renderHook` to do it.\n",{"id":2859,"difficulty":121,"q":2860,"a":2861},"hook-cleanup-test","How do you verify that a custom hook properly cleans up its effects on unmount?","Call `unmount()` from the `renderHook` result, then assert that side\neffects have been reversed — event listeners removed, timers cleared,\nsubscriptions cancelled.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport { useWindowResize } from '.\u002FuseWindowResize'\n\ntest('removes event listener on unmount', () => {\n  const addSpy = vi.spyOn(window, 'addEventListener')\n  const removeSpy = vi.spyOn(window, 'removeEventListener')\n\n  const { unmount } = renderHook(() => useWindowResize(() => {}))\n\n  expect(addSpy).toHaveBeenCalledWith('resize', expect.any(Function))\n\n  unmount()\n\n  \u002F\u002F Same handler that was added should be removed\n  expect(removeSpy).toHaveBeenCalledWith('resize', expect.any(Function))\n\n  addSpy.mockRestore()\n  removeSpy.mockRestore()\n})\n\ntest('cancels pending subscription on unmount', () => {\n  const unsubscribe = vi.fn()\n  const subscribe = vi.fn().mockReturnValue(unsubscribe)\n\n  const { unmount } = renderHook(() => useStoreSubscription(subscribe))\n\n  expect(subscribe).toHaveBeenCalledTimes(1)\n  unmount()\n  expect(unsubscribe).toHaveBeenCalledTimes(1)\n})\n```\n\nCleanup tests matter because leaking subscriptions or listeners cause:\n- Memory leaks in production.\n- \"Can't perform state update on unmounted component\" warnings.\n- State updates firing after the component is gone (act warnings in tests).\n\n**Rule of thumb:** Write an unmount cleanup test for every hook that\nregisters event listeners, starts timers, or opens subscriptions.\n",{"id":2863,"difficulty":121,"q":2864,"a":2865},"hook-error-test","How do you test that a hook throws an error under invalid conditions?","Wrap the `renderHook` call in a `try\u002Fcatch` or use\n`expect(() => ...).toThrow()`. Because React catches errors during\nrender and logs them, suppress `console.error` too.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport { useRequiredContext } from '.\u002FuseRequiredContext'\n\ntest('throws when used outside provider', () => {\n  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\n\n  expect(() => renderHook(() => useRequiredContext())).toThrow(\n    'useRequiredContext must be used inside RequiredContextProvider'\n  )\n\n  consoleSpy.mockRestore()\n})\n```\n\nFor hooks that validate their arguments:\n```js\ntest('throws for negative page size', () => {\n  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\n\n  expect(() =>\n    renderHook(() => usePagination({ page: 1, pageSize: -1, total: 100 }))\n  ).toThrow('pageSize must be positive')\n\n  consoleSpy.mockRestore()\n})\n```\n\nNote: React 18 with concurrent mode may not throw immediately on the\nfirst render — if you find that `expect(...).toThrow()` doesn't catch\nthe error, use an Error Boundary wrapper instead.\n\n**Rule of thumb:** Test the error guard condition (missing provider,\ninvalid args) so users get a clear message in development. Suppress\n`console.error` in these tests to keep output clean.\n",{"description":94},"Testing custom React hooks interview questions — renderHook, act, async hooks, context-dependent hooks, hook cleanup, refs, when to test hooks directly vs through components, and mocking hook dependencies.","react\u002Ftesting\u002Ftesting-custom-hooks","e-_K5wqYhGwL-YnIKLEUSaPIEC5MDbvOACpbC3E5Qr4",{"id":2871,"title":2872,"body":2873,"description":94,"difficulty":96,"extension":97,"framework":10,"frameworkSlug":8,"meta":2877,"navigation":100,"order":55,"path":2879,"questions":2880,"questionsCount":1495,"related":169,"seo":2953,"seoDescription":2954,"stem":2955,"subtopic":2956,"topic":28,"topicSlug":29,"updated":174,"__hash__":2957},"qa\u002Freact\u002Fcomponents\u002Flists-keys.md","Lists Keys",{"type":91,"value":2874,"toc":2875},[],{"title":94,"searchDepth":11,"depth":11,"links":2876},[],{"subtopicSlug":2878},"lists-keys","\u002Freact\u002Fcomponents\u002Flists-keys",[2881,2885,2889,2893,2897,2901,2905,2909,2913,2917,2921,2925,2929,2933,2937,2941,2945,2949],{"id":2882,"difficulty":96,"q":2883,"a":2884},"render-list","How do you render a list of items in React?","Use `Array.map` to transform a data array into an array of React elements.\nReact accepts an array of elements as a valid child.\n\n```jsx\nfunction FruitList({ fruits }) {\n  return (\n    \u003Cul>\n      {fruits.map(fruit => (\n        \u003Cli key={fruit.id}>{fruit.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n\n\u002F\u002F Usage\nconst fruits = [\n  { id: 1, name: 'Apple' },\n  { id: 2, name: 'Banana' },\n  { id: 3, name: 'Cherry' },\n]\n\u003CFruitList fruits={fruits} \u002F>\n```\n\nEach element in the mapped array must have a `key` prop so React can track\nit across renders.\n\n**Rule of thumb:** every `.map()` that produces JSX needs a `key` on the\noutermost returned element. If you forget it, React will warn in development\nand may produce incorrect diffs.\n",{"id":2886,"difficulty":96,"q":2887,"a":2888},"what-is-key","What is the key prop and why is it required for lists?","`key` is a **special React prop** that gives each element in a list a\nstable identity. React uses it during reconciliation to match elements\nbetween renders and decide what to update, move, or unmount.\n\n```jsx\n{todos.map(todo => (\n  \u003CTodoItem key={todo.id} todo={todo} \u002F>\n  \u002F\u002F         ▲ unique, stable identifier\n))}\n```\n\nWithout keys, React falls back to positional matching: element 0 in the\nold list = element 0 in the new list. If you insert or delete items, React\nmay reuse the wrong component instance, leading to bugs like stale state,\nwrong animation, or incorrect DOM content.\n\nKeys must be:\n- **Unique among siblings** (not globally unique).\n- **Stable** — the same item should have the same key across renders.\n\n**Rule of thumb:** always key from your data's natural unique identifier\n(database id, UUID, slug). Never use array index as a default.\n",{"id":2890,"difficulty":121,"q":2891,"a":2892},"index-as-key","What problems arise when you use the array index as a key?","Index keys break when the list can be **reordered, filtered, or have items\ninserted\u002Fdeleted**.\n\n```jsx\n\u002F\u002F ❌ Index key — dangerous for mutable lists\n{items.map((item, i) => \u003CRow key={i} data={item} \u002F>)}\n\n\u002F\u002F ✅ Stable id key\n{items.map(item => \u003CRow key={item.id} data={item} \u002F>)}\n```\n\nThe problem: React identifies components by key. If you delete item at\nindex 0, everything shifts down by one — the component at index 1 now has\nkey `0`, so React thinks it's the same component as the old index-0 element.\nReact will update props but **not reset state**. This causes:\n- Controlled input fields retaining the wrong value.\n- Animations starting from the wrong position.\n- Incorrect checkboxes or selection states.\n\n**Rule of thumb:** use index as key only when: the list is read-only and\nnever reordered, items have no stable IDs, and the items have no internal\nstate (pure display).\n",{"id":2894,"difficulty":121,"q":2895,"a":2896},"good-key","What makes a good key?","A good key is:\n1. **Unique among siblings** — two siblings can't share a key; siblings in\n   different lists can.\n2. **Stable** — the same data item has the same key across every render.\n3. **String or number** — React converts numbers to strings internally.\n\n```jsx\n\u002F\u002F ✅ Database primary key — best option\n\u003CItem key={item.id} \u002F>\n\n\u002F\u002F ✅ Slug\u002Fname if guaranteed unique per list\n\u003CTab key={tab.slug} \u002F>\n\n\u002F\u002F ✅ Composite key when no single field is unique\n\u003CCell key={`${row.id}-${col.name}`} \u002F>\n\n\u002F\u002F ❌ Math.random() — new key every render, defeats the purpose\n\u003CItem key={Math.random()} \u002F>\n\n\u002F\u002F ❌ Index — unstable when list mutates\n\u003CItem key={index} \u002F>\n```\n\nGenerating keys with `Math.random()` or `Date.now()` forces React to\nunmount and remount every item on every render — worse than no key at all.\n\n**Rule of thumb:** use the data's natural ID. If your data has no ID,\ngenerate one at the time the data is created (not at render time).\n",{"id":2898,"difficulty":96,"q":2899,"a":2900},"key-placement","Where does the key prop need to be placed?","The `key` must be on the **outermost element** returned from the map\ncallback — the element React is tracking. It does not need to be (and\ncannot be) read by the component that receives it.\n\n```jsx\n\u002F\u002F ✅ key on the outer element\n{products.map(p => (\n  \u003CProductCard key={p.id} product={p} \u002F>\n))}\n\n\u002F\u002F ❌ key on an inner element — React doesn't see it for list tracking\n{products.map(p => (\n  \u003Cdiv>\n    \u003CProductCard key={p.id} product={p} \u002F>  {\u002F* wrong position *\u002F}\n  \u003C\u002Fdiv>\n))}\n\n\u002F\u002F If the callback returns a Fragment, key goes on the Fragment\n{products.map(p => (\n  \u003CReact.Fragment key={p.id}>\n    \u003Cdt>{p.name}\u003C\u002Fdt>\n    \u003Cdd>{p.description}\u003C\u002Fdd>\n  \u003C\u002FReact.Fragment>\n))}\n```\n\n**Rule of thumb:** key goes on the tag you write in the `.map()` return,\nnot inside it.\n",{"id":2902,"difficulty":96,"q":2903,"a":2904},"key-global-unique","Does the key prop need to be globally unique?","No. Keys only need to be **unique among siblings** — elements within the\nsame list at the same level. The same key value can appear in a different\nlist elsewhere.\n\n```jsx\nfunction Page() {\n  return (\n    \u003C>\n      \u003Cul>\n        {fruits.map(f => \u003Cli key={f.id}>{f.name}\u003C\u002Fli>)}\n      \u003C\u002Ful>\n      \u003Cul>\n        {\u002F* key={1} here doesn't conflict with key={1} above *\u002F}\n        {vegetables.map(v => \u003Cli key={v.id}>{v.name}\u003C\u002Fli>)}\n      \u003C\u002Ful>\n    \u003C\u002F>\n  )\n}\n```\n\nReact tracks keys per list context, not globally across the component tree.\n\n**Rule of thumb:** your IDs only need to be unique within the array you're\nmapping. Composite keys like `${listName}-${item.id}` are redundant unless\nyou have a single flat array mixing different entity types.\n",{"id":2906,"difficulty":121,"q":2907,"a":2908},"reconciliation-keys","What is reconciliation and how do keys affect it?","**Reconciliation** is the process React uses to efficiently update the DOM\nby comparing the new virtual DOM tree with the previous one. React's\ndiffing algorithm has a key heuristic: elements of different types are\nalways replaced; elements of the same type are updated.\n\nKeys extend this heuristic to **lists**: React matches old and new elements\nby key, then decides:\n- Same key, same type → update props in place.\n- New key → mount a new component.\n- Missing key (was present before) → unmount the old component.\n- Same key, different position → React can reorder the DOM node efficiently.\n\n```\nBefore: [A(key=1), B(key=2), C(key=3)]\nAfter:  [C(key=3), A(key=1), B(key=2)]\n\nWith keys: React moves DOM nodes — no unmount\u002Fremount.\nWithout keys: React updates all three by position — potentially wrong.\n```\n\n**Rule of thumb:** correct keys make list operations O(n) instead of\nO(n²); they also prevent state from leaking to the wrong component when\nitems shift.\n",{"id":2910,"difficulty":121,"q":2911,"a":2912},"duplicate-keys","What happens if two siblings have the same key?","React warns in development and exhibits undefined behavior — typically it\nrenders only one of the duplicates and drops the other, or updates the\nwrong component on subsequent renders.\n\n```jsx\n\u002F\u002F ❌ Duplicate keys — unpredictable behavior\n{items.map(item => (\n  \u003CRow key={item.category} data={item} \u002F>\n  \u002F\u002F If multiple items share the same category, keys collide\n))}\n\n\u002F\u002F ✅ Use a truly unique field or compose a unique key\n{items.map(item => (\n  \u003CRow key={item.id} data={item} \u002F>\n))}\n```\n\nIn production, no warning is shown — the bug is silent. This is one reason\nto run development builds regularly.\n\n**Rule of thumb:** if you see the \"Encountered two children with the same\nkey\" warning in the console, your mapping produces non-unique keys — check\nwhich field you're using.\n",{"id":2914,"difficulty":121,"q":2915,"a":2916},"key-prop-readable","Can a component read its own key prop?","No. `key` is **reserved by React** and is not forwarded to the component\nas a prop. If you try to access `props.key` inside the component, you'll\nget `undefined`.\n\n```jsx\nfunction Row({ key, name }) {\n  \u002F\u002F key is always undefined here — React consumed it\n  return \u003Ctr>\u003Ctd>{name}\u003C\u002Ftd>\u003C\u002Ftr>\n}\n\n\u002F\u002F If the component needs the id, pass it explicitly\n{items.map(item => (\n  \u003CRow key={item.id} id={item.id} name={item.name} \u002F>\n))}\n```\n\nThe same is true for `ref` — both are meta-props handled by React before\nthe component sees its props.\n\n**Rule of thumb:** if a component needs its own identifier, pass it as a\nseparate prop (e.g. `id`). Treat `key` as infrastructure, not data.\n",{"id":2918,"difficulty":121,"q":2919,"a":2920},"no-id-data","How do you handle keys when your data has no unique IDs?","Options in rough order of preference:\n\n1. **Add an ID at the source.** If you control the API, add an `id` field.\n2. **Generate IDs when data enters the app** — not at render time.\n\n```jsx\n\u002F\u002F When you receive data, enrich it once\nimport { randomUUID } from 'crypto'\n\nconst items = rawData.map(d => ({ ...d, id: randomUUID() }))\n```\n\n3. **Composite key** from fields that together are unique:\n\n```jsx\n{countries.map(c => (\n  \u003CCountryRow key={`${c.code}-${c.region}`} country={c} \u002F>\n))}\n```\n\n4. **Index as last resort** — only if the list is static (never reordered\n   or mutated) and items have no internal state.\n\n**Rule of thumb:** generate IDs at data-entry time (API response handler,\nform submission), not inside `.map()`. A key generated in `.map()` is a\nnew key every render — React unmounts and remounts every item.\n",{"id":2922,"difficulty":121,"q":2923,"a":2924},"cost-of-no-key","What is the actual cost of not providing a key?","Without a key, React falls back to positional diffing, which causes:\n\n1. **Incorrect state association** — when items reorder, state (including\n   controlled input values, scroll position, open\u002Fclosed accordion) follows\n   the position, not the item.\n\n2. **Unnecessary re-renders and DOM mutations** — React may update the props\n   of an existing component rather than reorder it, doing more work.\n\n3. **Console warning** — in development, `Warning: Each child in a list\n   should have a unique \"key\" prop`.\n\n```jsx\n\u002F\u002F List re-ordered from [A, B, C] to [C, A, B]\n\u002F\u002F Without keys: React updates A→C, B→A, C→B (three updates + DOM writes)\n\u002F\u002F With keys: React moves DOM nodes (one reorder)\n```\n\nThe correctness bug is worse than the performance bug: stale input values\nor wrong selection state are visible to users.\n\n**Rule of thumb:** treat missing keys as a bug, not a warning. Fix them\nbefore they reach production.\n",{"id":2926,"difficulty":121,"q":2927,"a":2928},"render-component-list","How do you render a list of different component types based on data?","Use an object map or a switch inside the map callback to select the\ncomponent type dynamically.\n\n```jsx\nconst BLOCK_COMPONENTS = {\n  text:    TextBlock,\n  image:   ImageBlock,\n  video:   VideoBlock,\n  divider: DividerBlock,\n}\n\nfunction PageContent({ blocks }) {\n  return (\n    \u003Carticle>\n      {blocks.map(block => {\n        const Block = BLOCK_COMPONENTS[block.type]\n        if (!Block) return null\n        return \u003CBlock key={block.id} {...block} \u002F>\n      })}\n    \u003C\u002Farticle>\n  )\n}\n```\n\nNote that `Block` (uppercase) is required because JSX uses the case to\ndistinguish between a DOM element (string) and a component (variable reference).\n\n**Rule of thumb:** the object-map pattern scales better than a switch as\nthe number of types grows, and makes it easy to add new block types in one\nplace.\n",{"id":2930,"difficulty":121,"q":2931,"a":2932},"index-reorder-bug","What happens when list items reorder and you used index keys?","React matches old element at index N to new element at index N. If items\nreorder, the component at each position gets the props of the new item but\n**retains the state of the old item** — because from React's perspective\nit's the \"same\" component (same key = same index).\n\n```jsx\n\u002F\u002F Scenario: [Alice, Bob, Carol] → [Carol, Alice, Bob] (sorted)\n\u002F\u002F With index keys:\n\u002F\u002F   Index 0 was Alice, now Carol → React updates props: Alice→Carol\n\u002F\u002F   But Alice's text input still shows Alice's draft (state is index-0's)\n```\n\nVisually: the displayed name changes, but any controlled input value,\nopen dropdown, or animation state stays — it belongs to the position, not\nthe item.\n\n**Rule of thumb:** this is the canonical \"index key\" bug. Any time a list\ncan be sorted, filtered, or have items added\u002Fremoved, use stable IDs.\n",{"id":2934,"difficulty":121,"q":2935,"a":2936},"acceptable-index-key","When is it acceptable to use index as a key?","The React team's guidance: index as key is safe only when **all three**\nconditions hold:\n1. The list is **static** — it never reorders or has items inserted\u002Fdeleted.\n2. Items have **no stable IDs** in the data.\n3. The list is **never re-filtered** — count and order don't change.\n\n```jsx\n\u002F\u002F Acceptable: read-only, ordered, no unique id, no internal state\nconst STEPS = ['Install', 'Configure', 'Deploy']\n{STEPS.map((step, i) => (\n  \u003Cli key={i}>{step}\u003C\u002Fli>\n))}\n```\n\nIf the list comes from an API and may change at any time, don't use index.\n\n**Rule of thumb:** if you have to think about whether index is safe, your\ndata should have IDs. The safe scenarios are narrow; don't give yourself\npermission to use index keys broadly.\n",{"id":2938,"difficulty":121,"q":2939,"a":2940},"nested-lists","How do nested lists affect key requirements?","Each level of nesting is its own sibling context. Keys at one level don't\ninteract with keys at another. However, every `.map()` at every level\nneeds its own `key`.\n\n```jsx\nfunction Table({ rows }) {\n  return (\n    \u003Ctable>\n      \u003Ctbody>\n        {rows.map(row => (\n          \u003Ctr key={row.id}>   {\u002F* key for row siblings *\u002F}\n            {row.cells.map(cell => (\n              \u003Ctd key={cell.colId}>{cell.value}\u003C\u002Ftd>  {\u002F* key for cell siblings *\u002F}\n            ))}\n          \u003C\u002Ftr>\n        ))}\n      \u003C\u002Ftbody>\n    \u003C\u002Ftable>\n  )\n}\n```\n\n**Rule of thumb:** every `.map()` call needs a `key`, at every nesting\nlevel. The keys only need to be unique among siblings at that level —\n`row.id` and `cell.colId` don't need to be in different namespaces.\n",{"id":2942,"difficulty":121,"q":2943,"a":2944},"key-vs-ref","What is the difference between key and ref in React?","Both are special \"meta-props\" handled by React before the component sees\nits props, but they serve completely different purposes:\n\n| | `key` | `ref` |\n|---|---|---|\n| Purpose | List identity for reconciliation | Imperative access to DOM node \u002F component instance |\n| Readable by component | No (`props.key` is `undefined`) | Via `useRef` \u002F `forwardRef` |\n| When to use | Every element in a `.map()` list | Direct DOM manipulation, focus management, measuring |\n| Value type | String or number | Object from `useRef()` or callback |\n\n```jsx\nconst inputRef = useRef()\n\n\u002F\u002F key for list tracking, ref for focus imperatively\n{items.map(item => (\n  \u003Cinput key={item.id} ref={item.isFocused ? inputRef : null} \u002F>\n))}\n```\n\n**Rule of thumb:** `key` is for React's reconciler (list diffing); `ref`\nis for your code to poke the DOM. They are unrelated features that happen\nto both be invisible to `props`.\n",{"id":2946,"difficulty":121,"q":2947,"a":2948},"fragment-in-lists","What is the role of React.Fragment in lists?","When a list item needs to render **multiple sibling elements** without a\nwrapper DOM node, use `\u003CReact.Fragment key={...}>` — the short `\u003C>…\u003C\u002F>`\nsyntax doesn't support `key`.\n\n```jsx\nfunction DefinitionList({ items }) {\n  return (\n    \u003Cdl>\n      {items.map(item => (\n        \u002F\u002F Short fragment syntax doesn't accept key — use long form\n        \u003CReact.Fragment key={item.id}>\n          \u003Cdt>{item.term}\u003C\u002Fdt>\n          \u003Cdd>{item.definition}\u003C\u002Fdd>\n        \u003C\u002FReact.Fragment>\n      ))}\n    \u003C\u002Fdl>\n  )\n}\n```\n\nIf you wrap in a `\u003Cdiv>` instead, the resulting `\u003Cdl>\u003Cdiv>\u003Cdt>…\u003Cdd>…\u003C\u002Fdiv>\u003C\u002Fdl>`\nis invalid HTML and breaks styling.\n\n**Rule of thumb:** whenever you need `key` on a Fragment, use\n`\u003CReact.Fragment key={id}>`. It's the only valid way to key a fragment.\n",{"id":2950,"difficulty":208,"q":2951,"a":2952},"virtual-dom-diffing","How does React's virtual DOM use keys for diffing list elements?","React's reconciliation algorithm compares the previous and next virtual DOM\ntrees in a single pass per level. For lists specifically, it builds a\n**key-to-fiber map** of the old children, then for each child in the new\nlist it looks up the key:\n\n- **Key found, same type** → update props, keep the existing fiber (DOM\n  node), move it if position changed.\n- **Key found, different type** → unmount old, mount new.\n- **Key not found** → mount new fiber.\n- **Old key no longer in new list** → unmount.\n\n```\nOld: [\u003Cli key=\"a\">, \u003Cli key=\"b\">, \u003Cli key=\"c\">]\nNew: [\u003Cli key=\"c\">, \u003Cli key=\"a\">, \u003Cli key=\"b\">]\n\nReact: key \"c\" moved from index 2 → 0, DOM node reused.\n       keys \"a\" and \"b\" shifted, DOM nodes reused.\nResult: 0 unmounts, 0 mounts, 3 DOM moves.\n```\n\nWithout keys React would compare index-by-index and update all three\nelements' props even though the content is the same.\n\n**Rule of thumb:** keys let React reorder DOM nodes cheaply instead of\ndoing redundant prop patches. This is why keys are a performance AND\ncorrectness feature.\n",{"description":94},"React lists and keys interview questions — key prop, reconciliation, index as key pitfalls, stable keys, Fragment in lists, and rendering nested arrays.","react\u002Fcomponents\u002Flists-keys","Lists and Keys","PqeIWQPL16QElLjRR0piFnipmLYPvjvm8nl6oVP4PTU",{"id":2959,"title":2960,"body":2961,"description":94,"difficulty":208,"extension":97,"framework":10,"frameworkSlug":8,"meta":2965,"navigation":100,"order":55,"path":2966,"questions":2967,"questionsCount":493,"related":169,"seo":3026,"seoDescription":3027,"stem":3028,"subtopic":3029,"topic":20,"topicSlug":21,"updated":174,"__hash__":3030},"qa\u002Freact\u002Fhooks\u002Fusecallback-usememo.md","Usecallback Usememo",{"type":91,"value":2962,"toc":2963},[],{"title":94,"searchDepth":11,"depth":11,"links":2964},[],{},"\u002Freact\u002Fhooks\u002Fusecallback-usememo",[2968,2971,2974,2978,2982,2986,2990,2994,2998,3002,3006,3010,3014,3018,3022],{"id":1842,"difficulty":96,"q":2969,"a":2970},"What does useCallback do?","`useCallback` returns a **memoized version of a function** that only changes when\none of its dependencies changes. Calling it with the same dependencies returns the\nexact same function reference across renders.\n\n```jsx\nconst handleClick = useCallback(() => {\n  doSomething(id)\n}, [id]) \u002F\u002F new function only when `id` changes\n```\n\nWithout `useCallback`, a new function is created on every render. With it, the\nreference stays stable until `id` changes — which matters when passing the\nfunction to a memoized child or an effect dependency.\n\n**Rule of thumb:** `useCallback(fn, deps)` is `useMemo(() => fn, deps)`.\n",{"id":1838,"difficulty":96,"q":2972,"a":2973},"What does useMemo do?","`useMemo` caches the **return value** of a computation and recomputes it only when\nits dependencies change.\n\n```jsx\nconst sorted = useMemo(\n  () => [...items].sort(compare),\n  [items] \u002F\u002F recompute only when items changes\n)\n```\n\nOn renders where `items` hasn't changed, React skips calling the sort and returns\nthe cached result. Use it for expensive derivations from props or state.\n",{"id":2975,"difficulty":121,"q":2976,"a":2977},"callback-vs-memo","What is the difference between useCallback and useMemo?","They solve the same problem — referential stability across renders — for different\noutput types:\n\n- `useMemo` memoizes the **return value** of a function (any value).\n- `useCallback` memoizes the **function itself** (a specific case of useMemo).\n\n```jsx\n\u002F\u002F these two are equivalent\nconst fn = useCallback(() => compute(x), [x])\nconst fn = useMemo(() => () => compute(x), [x])\n\n\u002F\u002F useMemo for a computed value\nconst result = useMemo(() => compute(x), [x])\n\u002F\u002F useCallback for a stable function reference\nconst handler = useCallback(() => post(x), [x])\n```\n\nIn practice: `useCallback` for functions, `useMemo` for values.\n",{"id":2979,"difficulty":121,"q":2980,"a":2981},"referential-equality","What is referential equality and why does it matter for memoization?","JavaScript compares objects and functions by **reference** (memory address), not\nby content. Two identically-written functions are **not equal** unless they're\nthe same reference.\n\n```jsx\n(() => {}) === (() => {})  \u002F\u002F false — different references\nconst fn = () => {}\nfn === fn                  \u002F\u002F true — same reference\n```\n\nThis is why a new function created each render makes a child re-render even when\nnothing logically changed: `React.memo` sees a new prop reference and treats it\nas a change. `useCallback` returns the same reference, keeping memoization intact.\n",{"id":2983,"difficulty":121,"q":2984,"a":2985},"react-memo-interaction","How do useCallback and React.memo work together?","`React.memo` wraps a component and skips re-rendering when all its props are\n**reference-equal** to the last render. If you pass an unstable function prop\n(new reference each render), `React.memo` still re-renders — defeating the memo.\n\n```jsx\nconst Button = React.memo(function Button({ onClick }) {\n  return \u003Cbutton onClick={onClick}>click\u003C\u002Fbutton>\n})\n\nfunction Parent() {\n  \u002F\u002F new function each render -> Button re-renders despite memo\n  return \u003CButton onClick={() => console.log('hi')} \u002F>\n\n  \u002F\u002F stable function -> memo works\n  const handleClick = useCallback(() => console.log('hi'), [])\n  return \u003CButton onClick={handleClick} \u002F>\n}\n```\n\nThe pattern: `React.memo` on the child + `useCallback` on the function prop\npassed from the parent.\n",{"id":2987,"difficulty":208,"q":2988,"a":2989},"when-to-memoize","When should you actually use useMemo or useCallback?","Memoize when you have a **measurable performance problem**, not preemptively. The\nthree cases worth memoizing:\n\n1. **Expensive computation** — sorting\u002Ffiltering thousands of items, complex math.\n2. **Stable reference for a memoized child** — `React.memo` child that receives\n   functions or objects as props.\n3. **Effect dependency stabilization** — an object\u002Ffunction listed in `useEffect`\n   deps that would otherwise re-run the effect every render.\n\n```jsx\n\u002F\u002F worth it: sort runs O(n log n) on a large list\nconst sorted = useMemo(() => items.sort(compare), [items])\n\n\u002F\u002F not worth it: trivial, memo overhead exceeds savings\nconst doubled = useMemo(() => x * 2, [x])\n```\n\nProfile first. Every `useMemo`\u002F`useCallback` adds its own comparison cost and\ncognitive overhead — it's not free.\n",{"id":2991,"difficulty":121,"q":2992,"a":2993},"premature-memoization","Why is premature memoization harmful?","`useMemo` and `useCallback` have their own cost: React must store the cached\nvalue, compare dependency arrays on each render, and manage garbage collection.\nMemoizing cheap operations can make performance *worse* because the overhead\nexceeds the computation.\n\n```jsx\n\u002F\u002F overhead > savings for trivial computation\nconst label = useMemo(() => `Hello ${name}`, [name])\n\u002F\u002F just compute it: const label = `Hello ${name}`\n```\n\nAdditionally, memoization adds cognitive load — developers reading the code must\nreason about whether the deps array is correct. Default to plain computation;\nadd memoization only when profiling shows a real problem.\n",{"id":2995,"difficulty":208,"q":2996,"a":2997},"missing-deps","What happens if you omit a dependency from useMemo or useCallback?","The memoized value\u002Ffunction **closes over a stale value** — the one from the\nrender where it was last created. It never sees later updates to the omitted dep.\n\n```jsx\n\u002F\u002F user is omitted from deps -> greeting always shows the initial user.name\nconst greeting = useMemo(() => `Hi ${user.name}`, []) \u002F\u002F stale closure!\n\n\u002F\u002F correct\nconst greeting = useMemo(() => `Hi ${user.name}`, [user.name])\n```\n\nThe `react-hooks\u002Fexhaustive-deps` eslint rule catches this. Never suppress it to\n\"cache something forever\" — use a `useRef` if you intentionally want a value\nfrozen after mount.\n",{"id":2999,"difficulty":208,"q":3000,"a":3001},"effect-dep-stability","How does useCallback help stabilize effect dependencies?","A function defined in a component is recreated each render (new reference). If\nan effect depends on it, the effect re-runs every render. `useCallback` gives the\nfunction a stable reference that only changes when its own deps do.\n\n```jsx\n\u002F\u002F without useCallback: new function each render -> effect fires every render\nfunction fetchUser() { return api.get(userId) }\nuseEffect(() => { fetchUser().then(setUser) }, [fetchUser])\n\n\u002F\u002F with useCallback: stable function -> effect fires only when userId changes\nconst fetchUser = useCallback(() => api.get(userId), [userId])\nuseEffect(() => { fetchUser().then(setUser) }, [fetchUser])\n```\n\nAlternatively, move the function inside the effect to remove the dependency\nentirely — that's often cleaner.\n",{"id":3003,"difficulty":121,"q":3004,"a":3005},"object-stability-usememo","How do you stabilize an object prop with useMemo?","Wrap the object in `useMemo` so it keeps the same reference until its inputs\nchange. Without it, every render creates a new object even if the content is\nidentical — causing memoized children and dependent effects to fire.\n\n```jsx\n\u002F\u002F new object every render -> child always re-renders\n\u003CChart options={{ color: theme, size }} \u002F>\n\n\u002F\u002F stable reference -> child only re-renders when theme or size change\nconst options = useMemo(() => ({ color: theme, size }), [theme, size])\n\u003CChart options={options} \u002F>\n```\n\nThis is the object-version of `useCallback`. Prefer depending on the individual\nprimitives directly when possible — it's simpler.\n",{"id":3007,"difficulty":121,"q":3008,"a":3009},"memo-expensive-calc","How do you memoize an expensive derived list?","Compute the derived value with `useMemo`, depending on the raw data and any sort\u002F\nfilter parameters. The computation is skipped on renders where none of those\ninputs changed.\n\n```jsx\nconst filtered = useMemo(\n  () => products\n    .filter(p => p.category === activeCategory)\n    .sort((a, b) => a.price - b.price),\n  [products, activeCategory]\n)\n```\n\nWithout `useMemo`, the filter and sort run on every keystroke, click, and hover\nthat triggers a re-render — even when `products` and `activeCategory` haven't\nchanged.\n",{"id":3011,"difficulty":208,"q":3012,"a":3013},"usecallback-cost","What is the overhead of useCallback and useMemo?","Each call to `useCallback`\u002F`useMemo` on every render:\n1. Reads the stored deps array.\n2. Compares each dep with `Object.is`.\n3. Either returns the cached value or re-runs the computation.\n\nFor a tiny list or a trivial function, this comparison can cost **more** than just\nre-running the computation. React's own team has noted that memoization is a\nmicro-optimization and can be counter-productive on small values.\n\n```jsx\n\u002F\u002F comparison overhead likely exceeds multiplication cost\nconst n = useMemo(() => x * 2, [x])\n\u002F\u002F vs just: const n = x * 2\n```\n\nBenchmark before memoizing. Use React DevTools Profiler to identify actual\nexpensive renders, then target them specifically.\n",{"id":3015,"difficulty":121,"q":3016,"a":3017},"inline-function-no-memo","Is it always wrong to pass an inline function to a child component?","No. Inline functions are perfectly fine when:\n- The child is **not** wrapped in `React.memo`.\n- The function is not in an effect dependency array.\n- The child re-render is cheap (no deep tree, no expensive computation).\n\n```jsx\n\u002F\u002F fine: Button is not memoized, re-render is cheap\n\u003CButton onClick={() => setOpen(true)} \u002F>\n\n\u002F\u002F needed: DataGrid is memoized and re-renders are expensive\nconst handleSort = useCallback(col => setSort(col), [])\n\u003CDataGrid onSort={handleSort} \u002F>\n```\n\nDefaulting to `useCallback` for every handler is premature optimization that adds\nnoise. Add it when you have a real memoization need.\n",{"id":3019,"difficulty":121,"q":3020,"a":3021},"usememo-vs-state","Should you use useMemo or store the derived value in state?","Always prefer `useMemo` over storing derived data in state. State requires you to\nkeep two values in sync (the source and the derived copy), which can lead to\nbugs. `useMemo` derives the value automatically and stays in sync by definition.\n\n```jsx\n\u002F\u002F anti-pattern: derived state\nconst [items, setItems]   = useState([])\nconst [count, setCount]   = useState(0)\n\u002F\u002F must remember to call setCount every time setItems changes\n\n\u002F\u002F correct: derive in render\nconst [items, setItems] = useState([])\nconst count = useMemo(() => items.length, [items])\n\u002F\u002F or even simpler: const count = items.length (no memo needed here)\n```\n\nFor truly expensive derivations, add `useMemo`; for trivial ones, just compute\nthem inline — no hook needed.\n",{"id":3023,"difficulty":121,"q":3024,"a":3025},"remove-memoization","When should you remove a useMemo or useCallback?","Remove memoization when: (1) profiling shows the memoized path is never hot; (2)\nthe dependency array is so complex or unstable that the memo rarely hits; (3) the\nmemoized value is consumed by a non-memoized child (the stability gains nothing).\n\n```jsx\n\u002F\u002F useless: MutableChild re-renders anyway for other reasons\nconst handler = useCallback(fn, [deps])\n\u003CMutableChild onClick={handler} \u002F>\n\n\u002F\u002F useful: MemoChild skips renders when handler is stable\nconst handler = useCallback(fn, [deps])\n\u003CMemoChild onClick={handler} \u002F>\n```\n\nMemoization is not free — when it doesn't help, it only adds confusion. Keep the\ncode simple; add memos with evidence, remove them without regret.\n",{"description":94},"React useCallback and useMemo interview questions — referential stability, when to memoize, dependency arrays, performance pitfalls, and React.memo interaction.","react\u002Fhooks\u002Fusecallback-usememo","useCallback & useMemo","MXYFZ7Z2AY4kVT0nfmeMMGdgogSNiNQOKh6JZxq5veo",{"id":3032,"title":3033,"body":3034,"description":94,"difficulty":208,"extension":97,"framework":10,"frameworkSlug":8,"meta":3038,"navigation":100,"order":55,"path":3039,"questions":3040,"questionsCount":589,"related":169,"seo":3121,"seoDescription":3122,"stem":3123,"subtopic":3124,"topic":72,"topicSlug":74,"updated":420,"__hash__":3125},"qa\u002Freact\u002Fpatterns\u002Fforward-ref-imperative.md","Forward Ref Imperative",{"type":91,"value":3035,"toc":3036},[],{"title":94,"searchDepth":11,"depth":11,"links":3037},[],{},"\u002Freact\u002Fpatterns\u002Fforward-ref-imperative",[3041,3045,3049,3053,3057,3061,3065,3069,3073,3077,3081,3085,3089,3093,3097,3101,3105,3109,3113,3117],{"id":3042,"difficulty":96,"q":3043,"a":3044},"why-no-ref-on-function-components","Why can't you pass a ref directly to a function component the same way you pass one to a DOM element?","Function components are plain functions — they have no backing instance. When React\nrenders a class component it creates an object you can point a ref at. Function\ncomponents produce nothing but JSX, so there is **no object to attach the ref to**\nby default.\n\n```jsx\nfunction Input({ value, onChange }) {\n  return \u003Cinput value={value} onChange={onChange} \u002F>\n}\n\nfunction Parent() {\n  const ref = useRef(null)\n  \u002F\u002F ❌ ref will be null; React logs a warning\n  return \u003CInput ref={ref} value=\"\" onChange={() => {}} \u002F>\n}\n```\n\nTo let a parent reach inside, you must either wrap with `React.forwardRef` (React\n≤ 18) or accept `ref` as an explicit prop (React 19+).\n\n**Rule of thumb:** \"No instance\" means \"no implicit ref\" — you must opt in explicitly.\n",{"id":3046,"difficulty":96,"q":3047,"a":3048},"what-is-forward-ref","What does `React.forwardRef` do and what does its callback signature look like?","`React.forwardRef` wraps a render function and injects the parent-supplied ref as\na **second argument** alongside `props`. It returns a component that React treats\nas a normal component but also honours the ref.\n\n```jsx\nimport { forwardRef } from 'react'\n\n\u002F\u002F forwardRef(renderFn) — renderFn receives (props, ref)\nconst FancyInput = forwardRef(function FancyInput(props, ref) {\n  return (\n    \u003Cinput\n      ref={ref}          \u002F\u002F wire the forwarded ref to the DOM node\n      className=\"fancy\"\n      {...props}\n    \u002F>\n  )\n})\n\n\u002F\u002F Parent\nfunction Form() {\n  const inputRef = useRef(null)\n  return (\n    \u003C>\n      \u003CFancyInput ref={inputRef} placeholder=\"Type here\" \u002F>\n      \u003Cbutton onClick={() => inputRef.current.focus()}>Focus\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\nWithout `forwardRef`, `ref` would not appear in the render function's argument list\nat all — it is filtered out of `props` by React's reconciler.\n\n**Rule of thumb:** `forwardRef` = \"please pass the ref through to me as arg #2.\"\n",{"id":3050,"difficulty":121,"q":3051,"a":3052},"what-is-use-imperative-handle","What does `useImperativeHandle` do, and why would you use it instead of forwarding a raw DOM ref?","`useImperativeHandle(ref, createHandle, deps?)` **replaces** what the parent sees\nwhen it reads `ref.current`. Instead of the raw DOM node you expose a custom object\nwith only the methods you choose to surface. This is the controlled-API boundary\nbetween child and parent.\n\n```jsx\nconst VideoPlayer = forwardRef(function VideoPlayer({ src }, ref) {\n  const videoRef = useRef(null)\n\n  useImperativeHandle(ref, () => ({\n    \u002F\u002F expose a curated imperative API — not the raw \u003Cvideo> element\n    play()  { videoRef.current.play() },\n    pause() { videoRef.current.pause() },\n    seek(t) { videoRef.current.currentTime = t },\n  }))\n\n  return \u003Cvideo ref={videoRef} src={src} \u002F>\n})\n\nfunction Page() {\n  const player = useRef(null)\n  return (\n    \u003C>\n      \u003CVideoPlayer ref={player} src=\"\u002Fmovie.mp4\" \u002F>\n      \u003Cbutton onClick={() => player.current.play()}>Play\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\nThe parent can call `play`, `pause`, and `seek` but cannot touch the raw `\u003Cvideo>`\nnode — accidental DOM manipulation is prevented.\n\n**Rule of thumb:** `useImperativeHandle` = \"I'll expose only what I designed as my public API.\"\n",{"id":3054,"difficulty":121,"q":3055,"a":3056},"full-pattern-combined","Write the canonical \"custom input\" that uses `forwardRef` + `useImperativeHandle` together to expose `focus`, `blur`, and `clear`.","The standard pattern: keep the real DOM ref private inside the component; expose\nonly the three operations via `useImperativeHandle`.\n\n```jsx\nimport { forwardRef, useImperativeHandle, useRef } from 'react'\n\nconst SmartInput = forwardRef(function SmartInput(props, ref) {\n  const inputRef = useRef(null)          \u002F\u002F private — not leaked to parent\n\n  useImperativeHandle(ref, () => ({\n    focus() { inputRef.current.focus() },\n    blur()  { inputRef.current.blur()  },\n    clear() { inputRef.current.value = '' },\n  }), []) \u002F\u002F no deps — handle never changes\n\n  return \u003Cinput ref={inputRef} {...props} \u002F>\n})\n\n\u002F\u002F Usage\nfunction LoginForm() {\n  const usernameRef = useRef(null)\n  return (\n    \u003C>\n      \u003CSmartInput ref={usernameRef} placeholder=\"Username\" \u002F>\n      \u003Cbutton onClick={() => usernameRef.current.clear()}>Reset\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\nNote: the `deps` array of `useImperativeHandle` works exactly like `useEffect` —\nan empty array means the handle object is created once and never recreated.\n\n**Rule of thumb:** always keep the raw DOM ref private and return only the surface area the parent actually needs.\n",{"id":3058,"difficulty":121,"q":3059,"a":3060},"when-to-use-imperative-api","When is exposing an imperative handle appropriate, and when should you prefer props\u002Fstate instead?","Imperative handles suit **inherently imperative actions** that have no sensible\ndeclarative representation: focus management, scroll commands, starting\u002Fstopping\nmedia, triggering animations, opening\u002Fclosing portals when the open state is owned\nby the child.\n\n```jsx\n\u002F\u002F ✅ appropriate — \"start playing\" is an event, not a state the parent owns\nplayer.current.play()\n\n\u002F\u002F ✅ appropriate — programmatic focus after async validation\ninputRef.current.focus()\n\n\u002F\u002F ❌ avoid — visibility is declarative; use a prop instead\nmodalRef.current.setTitle('New title')   \u002F\u002F should be \u003CModal title={...} \u002F>\n\n\u002F\u002F ❌ avoid — data flow should go through props, not imperative setters\nlistRef.current.setItems(data)           \u002F\u002F should be \u003CList items={data} \u002F>\n```\n\nReact's model is \"data flows down via props.\" Imperative handles are the escape\nhatch for side-effectful actions that can't be expressed as a render-cycle change.\n\n**Rule of thumb:** if the parent needs to *trigger an action*, use a handle; if it needs to *change what renders*, use a prop.\n",{"id":3062,"difficulty":121,"q":3063,"a":3064},"exposing-full-dom-vs-custom-handle","What are the trade-offs between forwarding a raw DOM ref versus exposing a custom imperative handle?","Forwarding the raw DOM ref gives the parent **full access** — convenient, but\nit couples the parent to implementation details and allows accidental mutations.\n\n```jsx\n\u002F\u002F raw DOM ref — parent can do anything, including things you don't want\nconst RawInput = forwardRef((props, ref) => \u003Cinput ref={ref} {...props} \u002F>)\n\n\u002F\u002F custom handle — parent can only call what you expose\nconst SafeInput = forwardRef((props, ref) => {\n  const inner = useRef(null)\n  useImperativeHandle(ref, () => ({\n    focus: () => inner.current.focus(),\n    \u002F\u002F getSelectionRange, scrollIntoView, etc. — only what you choose\n  }))\n  return \u003Cinput ref={inner} {...props} \u002F>\n})\n```\n\nReasons to prefer a custom handle:\n- The child may replace its DOM implementation (e.g. swap `\u003Cinput>` for a\n  contenteditable div) without breaking the parent.\n- Prevents parents from reading `value` directly, bypassing React's state.\n- Documents the intended imperative API explicitly.\n\n**Rule of thumb:** forward the raw ref for thin wrappers; use a custom handle for components with a stable public contract.\n",{"id":3066,"difficulty":208,"q":3067,"a":3068},"typescript-forward-ref-typing","How do you correctly type a `forwardRef` component in TypeScript, including the `ref` and props generics?","`forwardRef` takes two type parameters: the **element \u002F handle type** and the\n**props type**. The order is `forwardRef\u003CHandleType, PropsType>`.\n\n```tsx\nimport { forwardRef, useRef, type ForwardedRef } from 'react'\n\ninterface InputProps {\n  label: string\n  defaultValue?: string\n}\n\n\u002F\u002F forwardRef\u003Chandle-type, props-type>\nconst LabelledInput = forwardRef\u003CHTMLInputElement, InputProps>(\n  function LabelledInput({ label, defaultValue }, ref) {\n    return (\n      \u003Clabel>\n        {label}\n        \u003Cinput ref={ref} defaultValue={defaultValue} \u002F>\n      \u003C\u002Flabel>\n    )\n  }\n)\n\n\u002F\u002F Consumer\nfunction Form() {\n  const ref = useRef\u003CHTMLInputElement>(null)\n  ref.current?.focus()   \u002F\u002F TS knows it's HTMLInputElement | null\n  return \u003CLabelledInput ref={ref} label=\"Name\" \u002F>\n}\n```\n\nIf you use `useImperativeHandle`, replace `HTMLInputElement` with your handle\ninterface (e.g. `{ focus(): void; clear(): void }`).\n\n**Rule of thumb:** `forwardRef\u003CHandleType, PropsType>` — handle first, props second.\n",{"id":3070,"difficulty":208,"q":3071,"a":3072},"typescript-imperative-handle-typing","How do you type `useImperativeHandle` when the exposed handle is a custom interface?","Define an interface for the handle, use it as the first generic to `forwardRef`,\nand `useImperativeHandle` will enforce that `createHandle` returns a conforming\nobject.\n\n```tsx\nimport { forwardRef, useImperativeHandle, useRef } from 'react'\n\n\u002F\u002F 1. Define the public imperative API\nexport interface ModalHandle {\n  open(): void\n  close(): void\n  isOpen(): boolean\n}\n\ninterface ModalProps {\n  title: string\n  children: React.ReactNode\n}\n\n\u002F\u002F 2. Use ModalHandle as the first generic\nconst Modal = forwardRef\u003CModalHandle, ModalProps>(function Modal(\n  { title, children },\n  ref\n) {\n  const [visible, setVisible] = React.useState(false)\n\n  useImperativeHandle(ref, () => ({\n    open:   () => setVisible(true),\n    close:  () => setVisible(false),\n    isOpen: () => visible,\n  }))\n\n  if (!visible) return null\n  return \u003Cdialog open>\u003Ch2>{title}\u003C\u002Fh2>{children}\u003C\u002Fdialog>\n})\n\n\u002F\u002F 3. Parent types the ref with the handle interface\nfunction App() {\n  const modalRef = React.useRef\u003CModalHandle>(null)\n  return (\n    \u003C>\n      \u003CModal ref={modalRef} title=\"Alert\">Are you sure?\u003C\u002FModal>\n      \u003Cbutton onClick={() => modalRef.current?.open()}>Show\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\n**Rule of thumb:** export the handle interface alongside the component so consumers can type their refs without importing internals.\n",{"id":3074,"difficulty":208,"q":3075,"a":3076},"react-19-ref-as-prop","How does React 19 change ref handling, and is `forwardRef` still needed?","In React 19, **`ref` is a regular prop**. Function components can receive `ref`\ndirectly in their props object — no `forwardRef` wrapper required. `forwardRef`\nstill works for backwards compatibility but is considered legacy and will eventually\nbe deprecated.\n\n```tsx\n\u002F\u002F React 19 — ref is just another prop\nfunction FancyInput({ ref, ...props }: React.ComponentProps\u003C'input'> & {\n  ref?: React.Ref\u003CHTMLInputElement>\n}) {\n  return \u003Cinput ref={ref} className=\"fancy\" {...props} \u002F>\n}\n\n\u002F\u002F React ≤ 18 — must use forwardRef\nconst FancyInputLegacy = React.forwardRef\u003CHTMLInputElement, React.ComponentProps\u003C'input'>>(\n  function FancyInputLegacy(props, ref) {\n    return \u003Cinput ref={ref} className=\"fancy\" {...props} \u002F>\n  }\n)\n```\n\n`useImperativeHandle` continues to work in React 19 — you still call it inside the\ncomponent with the `ref` prop directly.\n\n**Rule of thumb:** for new React 19 codebases, skip `forwardRef` and accept `ref` as a prop; keep `forwardRef` when targeting older React versions.\n",{"id":3078,"difficulty":121,"q":3079,"a":3080},"memo-and-forward-ref","How do `React.memo` and `forwardRef` interact, and in what order should you compose them?","Both `forwardRef` and `memo` are higher-order wrappers that return a new component.\nYou must apply `forwardRef` **first** (innermost), then wrap with `memo` so that\nmemoisation sees the already-forwarded component.\n\n```jsx\nimport { forwardRef, memo, useRef } from 'react'\n\n\u002F\u002F Step 1 — enable ref forwarding\nconst BaseInput = forwardRef(function BaseInput(props, ref) {\n  return \u003Cinput ref={ref} {...props} \u002F>\n})\n\n\u002F\u002F Step 2 — memoize the forwarded component\nconst MemoInput = memo(BaseInput)\n\n\u002F\u002F Usage — both ref forwarding and memoisation work correctly\nfunction Parent() {\n  const ref = useRef(null)\n  return \u003CMemoInput ref={ref} value=\"\" onChange={() => {}} \u002F>\n}\n```\n\nIf you apply `memo` first and then `forwardRef`, the outer wrapper still works\nin practice (React resolves the chain), but the conventional and explicit order is\n`memo(forwardRef(...))`.\n\n**Rule of thumb:** `memo(forwardRef(component))` — ref first, memo second.\n",{"id":3082,"difficulty":121,"q":3083,"a":3084},"modal-open-close-pattern","Show a real-world \"modal open\u002Fclose\" pattern where the parent triggers `open()` and `close()` imperatively.","This pattern is appropriate when the **open\u002Fclosed state lives inside the modal**\nitself (e.g. it manages its own transition) and the parent only needs to command it.\n\n```jsx\nconst ConfirmModal = forwardRef(function ConfirmModal({ onConfirm }, ref) {\n  const [open, setOpen] = React.useState(false)\n  const dialogRef = useRef(null)\n\n  useImperativeHandle(ref, () => ({\n    open:  () => { setOpen(true);  dialogRef.current?.showModal() },\n    close: () => { setOpen(false); dialogRef.current?.close()     },\n  }))\n\n  if (!open) return null\n  return (\n    \u003Cdialog ref={dialogRef}>\n      \u003Cp>Are you sure?\u003C\u002Fp>\n      \u003Cbutton onClick={() => { onConfirm(); setOpen(false) }}>Yes\u003C\u002Fbutton>\n      \u003Cbutton onClick={() => setOpen(false)}>No\u003C\u002Fbutton>\n    \u003C\u002Fdialog>\n  )\n})\n\nfunction DeleteButton({ id }) {\n  const modal = useRef(null)\n  return (\n    \u003C>\n      \u003Cbutton onClick={() => modal.current.open()}>Delete\u003C\u002Fbutton>\n      \u003CConfirmModal ref={modal} onConfirm={() => deleteItem(id)} \u002F>\n    \u003C\u002F>\n  )\n}\n```\n\n**Rule of thumb:** use this only when open\u002Fclose state is truly private to the modal; if the parent must know whether the modal is open, lift state or use a prop instead.\n",{"id":3086,"difficulty":96,"q":3087,"a":3088},"focus-management-pattern","Demonstrate using `forwardRef` for focus management in a form with multiple inputs.","Focus management is the canonical use case — moving focus programmatically after\nvalidation errors or submit actions.\n\n```jsx\nconst Field = forwardRef(function Field({ label, error, ...inputProps }, ref) {\n  return (\n    \u003Cdiv className={error ? 'field field--error' : 'field'}>\n      \u003Clabel>{label}\u003C\u002Flabel>\n      \u003Cinput ref={ref} {...inputProps} \u002F>\n      {error && \u003Cspan className=\"error\">{error}\u003C\u002Fspan>}\n    \u003C\u002Fdiv>\n  )\n})\n\nfunction SignupForm() {\n  const emailRef    = useRef(null)\n  const passwordRef = useRef(null)\n\n  function handleSubmit(e) {\n    e.preventDefault()\n    const errors = validate(e.target)\n    if (errors.email)    { emailRef.current.focus();    return }\n    if (errors.password) { passwordRef.current.focus(); return }\n    submitForm(e.target)\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003CField ref={emailRef}    label=\"Email\"    type=\"email\"    \u002F>\n      \u003CField ref={passwordRef} label=\"Password\" type=\"password\" \u002F>\n      \u003Cbutton type=\"submit\">Sign up\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n**Rule of thumb:** reach for `forwardRef` when a parent orchestrates focus across several child inputs after async feedback.\n",{"id":3090,"difficulty":208,"q":3091,"a":3092},"animation-handle-pattern","How would you expose animation controls (play, pause, reset) from a child component via `useImperativeHandle`?","Animation libraries (GSAP, Framer Motion) maintain internal timelines. Exposing\n`play`, `pause`, and `reset` via a handle keeps the animation logic encapsulated\nwhile giving parent components a clean trigger surface.\n\n```jsx\nimport gsap from 'gsap'\n\nconst AnimatedCard = forwardRef(function AnimatedCard({ children }, ref) {\n  const cardRef = useRef(null)\n  const tlRef   = useRef(null)         \u002F\u002F private timeline\n\n  useEffect(() => {\n    tlRef.current = gsap.timeline({ paused: true })\n      .fromTo(cardRef.current,\n        { opacity: 0, y: 40 },\n        { opacity: 1, y: 0, duration: 0.4 }\n      )\n  }, [])\n\n  useImperativeHandle(ref, () => ({\n    play:  () => tlRef.current.play(),\n    pause: () => tlRef.current.pause(),\n    reset: () => tlRef.current.restart().pause(),\n  }))\n\n  return \u003Cdiv ref={cardRef}>{children}\u003C\u002Fdiv>\n})\n\nfunction Demo() {\n  const card = useRef(null)\n  return (\n    \u003C>\n      \u003CAnimatedCard ref={card}>Hello\u003C\u002FAnimatedCard>\n      \u003Cbutton onClick={() => card.current.play()}>Animate in\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\n**Rule of thumb:** expose semantic action names (`play`, `pause`) not raw library handles — the parent shouldn't need to know which animation library you use.\n",{"id":3094,"difficulty":208,"q":3095,"a":3096},"forwarded-ref-type-forwarded-ref","What is `ForwardedRef\u003CT>` in TypeScript and when do you need it over `Ref\u003CT>`?","`React.ForwardedRef\u003CT>` is the exact type of the second argument received by a\n`forwardRef` render function. It is a union: `RefCallback\u003CT> | MutableRefObject\u003CT> | null`.\n`React.Ref\u003CT>` is a wider union that also includes `string` refs (legacy) and is\nused for *passing* refs to elements.\n\n```tsx\nimport { forwardRef, type ForwardedRef } from 'react'\n\n\u002F\u002F ForwardedRef\u003CT> is correct for the parameter you *receive*\nfunction BaseInput(\n  props: React.ComponentProps\u003C'input'>,\n  ref: ForwardedRef\u003CHTMLInputElement>  \u002F\u002F not Ref\u003C> — this is what forwardRef gives you\n) {\n  return \u003Cinput ref={ref} {...props} \u002F>\n}\nexport default forwardRef(BaseInput)\n\n\u002F\u002F If you want to accept a ref in a helper that isn't a forwardRef component:\nfunction mergeRefs\u003CT>(...refs: ForwardedRef\u003CT>[]): React.RefCallback\u003CT> {\n  return (node) => {\n    refs.forEach(ref => {\n      if (typeof ref === 'function') ref(node)\n      else if (ref) ref.current = node\n    })\n  }\n}\n```\n\n**Rule of thumb:** use `ForwardedRef\u003CT>` for the ref parameter inside `forwardRef`; use `Ref\u003CT>` or `RefObject\u003CT>` in props interfaces for refs the component *accepts* as props.\n",{"id":3098,"difficulty":121,"q":3099,"a":3100},"testing-forward-ref-components","How do you test a `forwardRef` component that exposes an imperative handle in React Testing Library?","Use `React.createRef()` (or `useRef` inside a wrapper component) to capture the\nhandle, then assert on its methods.\n\n```jsx\nimport { render, screen } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { createRef } from 'react'\nimport SmartInput from '.\u002FSmartInput'\n\ntest('clear() empties the input', async () => {\n  const ref = createRef()\n  render(\u003CSmartInput ref={ref} defaultValue=\"hello\" data-testid=\"inp\" \u002F>)\n\n  \u002F\u002F verify initial state\n  expect(screen.getByTestId('inp').value).toBe('hello')\n\n  \u002F\u002F call the imperative method\n  ref.current.clear()\n\n  \u002F\u002F assert DOM changed\n  expect(screen.getByTestId('inp').value).toBe('')\n})\n\ntest('focus() moves focus to the input', () => {\n  const ref = createRef()\n  render(\u003CSmartInput ref={ref} data-testid=\"inp\" \u002F>)\n  ref.current.focus()\n  expect(screen.getByTestId('inp')).toHaveFocus()\n})\n```\n\nYou don't need to test the internal DOM structure — only that the exposed handle\nmethods produce the expected observable behaviour.\n\n**Rule of thumb:** test the contract of the handle (what it does), not how it is implemented.\n",{"id":3102,"difficulty":121,"q":3103,"a":3104},"deps-array-use-imperative-handle","When should you provide a non-empty `deps` array to `useImperativeHandle`, and what happens if you omit it?","`useImperativeHandle(ref, createHandle, deps)` re-runs `createHandle` whenever a\ndependency changes, replacing `ref.current` with a new object. With an empty array\n`[]`, the handle is created once. With no deps array at all, it reruns on every render.\n\n```jsx\nconst Counter = forwardRef(function Counter(props, ref) {\n  const [count, setCount] = useState(0)\n\n  useImperativeHandle(ref, () => ({\n    increment: () => setCount(c => c + 1),\n    \u002F\u002F getCount captures the current count via closure —\n    \u002F\u002F must be in deps so the handle refreshes when count changes\n    getCount:  () => count,\n  }), [count]) \u002F\u002F ← re-create handle when count changes\n\n  return \u003Cp>{count}\u003C\u002Fp>\n})\n```\n\nIf `getCount` were in a stale closure (deps omitted or `[]`) it would always return\nthe initial value. Omitting deps entirely causes a new handle object every render,\nwhich is wasteful but not incorrect.\n\n**Rule of thumb:** include any state or prop that the handle methods read via closure; use `[]` only when the methods rely solely on refs or setters (which are stable).\n",{"id":3106,"difficulty":208,"q":3107,"a":3108},"multiple-refs-same-element","How do you support both a forwarded ref and an internal ref pointing at the same DOM node — the \"mergeRefs\" pattern?","A DOM node can only have one `ref` prop. When a component needs its own local ref\n*and* must forward the parent's ref to the same node, you merge them with a\ncallback ref.\n\n```jsx\nfunction mergeRefs(...refs) {\n  return (node) => {\n    refs.forEach(ref => {\n      if (typeof ref === 'function') ref(node)      \u002F\u002F callback ref\n      else if (ref != null) ref.current = node      \u002F\u002F object ref\n    })\n  }\n}\n\nconst ScrollableList = forwardRef(function ScrollableList(props, forwardedRef) {\n  const localRef = useRef(null)  \u002F\u002F needed for internal scroll logic\n\n  function scrollToTop() {\n    localRef.current?.scrollTo({ top: 0, behavior: 'smooth' })\n  }\n\n  return (\n    \u003Cul\n      ref={mergeRefs(localRef, forwardedRef)} \u002F\u002F both refs point at \u003Cul>\n      {...props}\n    \u002F>\n  )\n})\n```\n\nLibraries like `useMergedRef` (react-merge-refs) do exactly this. React 19's\ncleanup-returning ref callbacks make the pattern cleaner still.\n\n**Rule of thumb:** when you need two refs on one node, always merge — never pick one and lose the other.\n",{"id":3110,"difficulty":121,"q":3111,"a":3112},"avoid-imperative-anti-patterns","What are the most common mistakes developers make when using `forwardRef` and `useImperativeHandle`?","Five common mistakes:\n\n1. **Exposing the raw DOM node when a custom handle is safer** — gives the parent\n   unrestricted access.\n2. **Using an imperative handle to pass data** — `ref.current.getUser()` should be\n   `const { user } = useUser()` or a prop.\n3. **Forgetting to memoize the handle** when it closes over frequently-changing\n   state without listing it in deps.\n4. **Wrapping every component in `forwardRef`** \"just in case\" — most components\n   are controlled via props and don't need a ref at all.\n5. **Not cleaning up** — if the handle exposes subscriptions or timers, they should\n   be cleared in a `useEffect` cleanup, not in the handle method itself.\n\n```jsx\n\u002F\u002F ❌ anti-pattern — data fetch through an imperative handle\nconst DataList = forwardRef((_, ref) => {\n  useImperativeHandle(ref, () => ({\n    getData: async () => fetchData(),  \u002F\u002F parent should just receive data as a prop\n  }))\n  return \u003Cul \u002F>\n})\n\n\u002F\u002F ✅ prefer — data flows through props \u002F context\nfunction DataList({ data }) {\n  return \u003Cul>{data.map(item => \u003Cli key={item.id}>{item.name}\u003C\u002Fli>)}\u003C\u002Ful>\n}\n```\n\n**Rule of thumb:** before adding an imperative handle, ask \"can I achieve this with a prop, callback, or state?\" — if yes, do that instead.\n",{"id":3114,"difficulty":96,"q":3115,"a":3116},"display-name-forward-ref","How do you set a meaningful display name on a `forwardRef` component for React DevTools?","React DevTools shows the display name in the component tree. Anonymous `forwardRef`\ncalls appear as `ForwardRef` — unhelpful for debugging.\n\n```jsx\n\u002F\u002F Option 1 — named function expression (preferred)\nconst TextInput = forwardRef(function TextInput(props, ref) {\n  return \u003Cinput ref={ref} {...props} \u002F>\n})\n\u002F\u002F DevTools shows: TextInput\n\n\u002F\u002F Option 2 — set displayName explicitly\nconst TextInput2 = forwardRef((props, ref) => (\n  \u003Cinput ref={ref} {...props} \u002F>\n))\nTextInput2.displayName = 'TextInput'\n\n\u002F\u002F Option 3 — in a factory helper, set displayName dynamically\nfunction createForwardedInput(displayName) {\n  const C = forwardRef((props, ref) => \u003Cinput ref={ref} {...props} \u002F>)\n  C.displayName = displayName\n  return C\n}\n```\n\nNamed function expressions (Option 1) are the cleanest because the name is\ninferred automatically without an extra line.\n\n**Rule of thumb:** always use a named function inside `forwardRef` — you get a readable DevTools tree for free.\n",{"id":3118,"difficulty":121,"q":3119,"a":3120},"scroll-into-view-pattern","How would you implement a `scrollIntoView()` imperative handle to let a parent scroll a child list item into view?","A list component that manages its own DOM can expose a `scrollTo(index)` method\nso the parent doesn't have to reach into its internals.\n\n```jsx\nconst VirtualList = forwardRef(function VirtualList({ items }, ref) {\n  const rowRefs = useRef([])   \u002F\u002F array of row DOM nodes\n\n  useImperativeHandle(ref, () => ({\n    \u002F\u002F scrollTo(index) — brings row at `index` into the viewport\n    scrollTo(index) {\n      rowRefs.current[index]?.scrollIntoView({\n        behavior: 'smooth',\n        block: 'nearest',\n      })\n    },\n  }), []) \u002F\u002F stable — no deps needed; rowRefs.current is mutable\n\n  return (\n    \u003Cul>\n      {items.map((item, i) => (\n        \u003Cli key={item.id} ref={el => { rowRefs.current[i] = el }}>\n          {item.label}\n        \u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n})\n\nfunction SearchResults({ results, activeIndex }) {\n  const listRef = useRef(null)\n  useEffect(() => {\n    listRef.current?.scrollTo(activeIndex)\n  }, [activeIndex])\n\n  return \u003CVirtualList ref={listRef} items={results} \u002F>\n}\n```\n\n**Rule of thumb:** when the child owns a collection of DOM nodes, expose a semantic `scrollTo(index)` rather than exposing the raw refs array.\n",{"description":94},"React forwardRef and useImperativeHandle interview questions — ref forwarding, imperative API design, focus control, animation handles, and TypeScript ref typing.","react\u002Fpatterns\u002Fforward-ref-imperative","forwardRef & useImperativeHandle","3l-TC_wGYc6rODTadiYbBCLWtDVvE0FvoJi49qa5mBg",{"id":3127,"title":3128,"body":3129,"description":94,"difficulty":208,"extension":97,"framework":10,"frameworkSlug":8,"meta":3133,"navigation":100,"order":55,"path":3135,"questions":3136,"questionsCount":493,"related":169,"seo":3196,"seoDescription":3197,"stem":3198,"subtopic":3199,"topic":45,"topicSlug":47,"updated":420,"__hash__":3200},"qa\u002Freact\u002Frendering-and-performance\u002Fsuspense-concurrent.md","Suspense Concurrent",{"type":91,"value":3130,"toc":3131},[],{"title":94,"searchDepth":11,"depth":11,"links":3132},[],{"subtopicSlug":3134},"suspense-concurrent","\u002Freact\u002Frendering-and-performance\u002Fsuspense-concurrent",[3137,3141,3144,3148,3152,3156,3160,3164,3168,3172,3176,3180,3184,3188,3192],{"id":3138,"difficulty":96,"q":3139,"a":3140},"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":2496,"difficulty":96,"q":3142,"a":3143},"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":3145,"difficulty":121,"q":3146,"a":3147},"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":3149,"difficulty":121,"q":3150,"a":3151},"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":3153,"difficulty":121,"q":3154,"a":3155},"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":3157,"difficulty":96,"q":3158,"a":3159},"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":3161,"difficulty":121,"q":3162,"a":3163},"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":3165,"difficulty":208,"q":3166,"a":3167},"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":3169,"difficulty":208,"q":3170,"a":3171},"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":3173,"difficulty":121,"q":3174,"a":3175},"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":3177,"difficulty":208,"q":3178,"a":3179},"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":3181,"difficulty":121,"q":3182,"a":3183},"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":3185,"difficulty":208,"q":3186,"a":3187},"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":3189,"difficulty":208,"q":3190,"a":3191},"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":3193,"difficulty":208,"q":3194,"a":3195},"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":94},"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",{"id":3202,"title":3203,"body":3204,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":3208,"navigation":100,"order":64,"path":3209,"questions":3210,"questionsCount":493,"related":169,"seo":3267,"seoDescription":3268,"stem":3269,"subtopic":3270,"topic":20,"topicSlug":21,"updated":174,"__hash__":3271},"qa\u002Freact\u002Fhooks\u002Fuseref.md","Useref",{"type":91,"value":3205,"toc":3206},[],{"title":94,"searchDepth":11,"depth":11,"links":3207},[],{},"\u002Freact\u002Fhooks\u002Fuseref",[3211,3214,3217,3221,3224,3228,3231,3235,3239,3243,3247,3251,3255,3259,3263],{"id":2412,"difficulty":96,"q":3212,"a":3213},"What does useRef return?","`useRef` returns a **mutable object** with a single `.current` property,\ninitialized to the argument you pass. The object persists for the **full lifetime\nof the component** — across renders — and changing `.current` does **not**\ntrigger a re-render.\n\n```jsx\nconst ref = useRef(null)\n\u002F\u002F ref.current is null until attached to a DOM element or set manually\nref.current = 42 \u002F\u002F silent mutation, no re-render\n```\n\nTwo primary uses: (1) holding a DOM element reference and (2) storing a mutable\ninstance variable (like a timer ID or a previous value) that must survive renders\nwithout causing them.\n",{"id":2416,"difficulty":121,"q":3215,"a":3216},"What is the difference between useRef and useState?","| | useState | useRef |\n|---|---|---|\n| Triggers re-render | Yes | No |\n| Value persists across renders | Yes | Yes |\n| Used to drive the UI | Yes | No |\n| Mutable | No (replace via setter) | Yes (mutate .current directly) |\n\n```jsx\nconst [count, setCount] = useState(0) \u002F\u002F UI re-renders when count changes\nconst renders = useRef(0)              \u002F\u002F bookkeeping; changing it is invisible\nrenders.current++\n```\n\nIf the screen needs to show the value, it belongs in state. If you need to\nremember something between renders without affecting the UI, use a ref.\n",{"id":3218,"difficulty":96,"q":3219,"a":3220},"dom-ref","How do you access a DOM element with useRef?","Pass the ref object to a JSX element's `ref` attribute. After the component\nmounts, `ref.current` holds the underlying DOM node.\n\n```jsx\nconst inputRef = useRef(null)\nuseEffect(() => {\n  inputRef.current.focus() \u002F\u002F focus the input after mount\n}, [])\nreturn \u003Cinput ref={inputRef} \u002F>\n```\n\n`ref.current` is `null` during the first render (the DOM node doesn't exist yet).\nIt's populated after the component mounts and set back to `null` on unmount.\n",{"id":2420,"difficulty":208,"q":3222,"a":3223},"What is a callback ref and when do you need one?","Instead of a ref object, you can pass a **function** to `ref`. React calls it\nwith the DOM node when it mounts (and `null` when it unmounts). This is useful\nwhen you need to know the **exact moment** the element attaches.\n\n```jsx\nfunction MeasureDiv() {\n  const [height, setHeight] = useState(null)\n  const measuredRef = useCallback(node => {\n    if (node !== null) setHeight(node.getBoundingClientRect().height)\n  }, [])\n  return \u003Cdiv ref={measuredRef}>content\u003C\u002Fdiv>\n}\n```\n\nA regular ref object doesn't notify you when the node appears or changes — that's\nthe gap callback refs fill. They're also the right approach for conditional\nelements or lists where the number of nodes changes.\n",{"id":3225,"difficulty":208,"q":3226,"a":3227},"forward-ref","What is forwardRef and when do you use it?","By default, you can't attach a `ref` to a custom function component — the ref\ngoes to the component instance, which doesn't exist for function components.\n`forwardRef` lets a parent reach a DOM node **inside** the child.\n\n```jsx\nconst Input = forwardRef(function Input({ label }, ref) {\n  return (\n    \u003Clabel>\n      {label}\n      \u003Cinput ref={ref} \u002F>\n    \u003C\u002Flabel>\n  )\n})\n\nfunction Form() {\n  const inputRef = useRef(null)\n  return \u003CInput label=\"Name\" ref={inputRef} \u002F>\n  \u002F\u002F inputRef.current is the \u003Cinput> DOM node\n}\n```\n\nUse `forwardRef` for reusable input or UI components where the parent legitimately\nneeds direct DOM access (focus management, scroll, measurement).\n",{"id":2428,"difficulty":208,"q":3229,"a":3230},"What is useImperativeHandle?","`useImperativeHandle` customizes what a parent sees when it holds a `ref` to your\ncomponent — instead of a DOM node, you expose a controlled API of your choosing.\n\n```jsx\nconst Dialog = forwardRef(function Dialog(props, ref) {\n  const [open, setOpen] = useState(false)\n  useImperativeHandle(ref, () => ({\n    open:  () => setOpen(true),\n    close: () => setOpen(false),\n  }))\n  return open ? \u003Cdiv>dialog\u003C\u002Fdiv> : null\n})\n\nfunction App() {\n  const dialogRef = useRef(null)\n  return (\n    \u003C>\n      \u003Cbutton onClick={() => dialogRef.current.open()}>show\u003C\u002Fbutton>\n      \u003CDialog ref={dialogRef} \u002F>\n    \u003C\u002F>\n  )\n}\n```\n\nThis is an **escape hatch** for imperative integration. Prefer declarative props\nin most cases; use `useImperativeHandle` when consumers genuinely need to trigger\nbehavior imperatively (animations, focus sequences, scroll).\n",{"id":3232,"difficulty":96,"q":3233,"a":3234},"ref-timer","How do you store a timer ID in a ref?","Assign the timer ID to `ref.current` inside an effect. It persists across renders\nwithout triggering them, and you can read it in the cleanup function.\n\n```jsx\nconst timerId = useRef(null)\n\nuseEffect(() => {\n  timerId.current = setInterval(tick, 1000)\n  return () => clearInterval(timerId.current)\n}, [])\n\nfunction stop() {\n  clearInterval(timerId.current)\n}\n```\n\nUsing state for a timer ID would cause a re-render each time you start or stop\nthe interval — unnecessary since the ID never appears in the UI.\n",{"id":3236,"difficulty":121,"q":3237,"a":3238},"prev-value","How do you track the previous value of a prop or state with useRef?","Store the previous value in a ref, updating it at the end of each render via an\neffect (or by manually updating it after using it in the render body).\n\n```jsx\nfunction usePrevious(value) {\n  const ref = useRef()\n  useEffect(() => {\n    ref.current = value  \u002F\u002F update after render\n  })\n  return ref.current     \u002F\u002F returns the value from the previous render\n}\n\nconst prevCount = usePrevious(count)\n```\n\nThe effect runs after the render, so `ref.current` on the current render still\nholds the value from the previous render — which is what the caller wants.\n",{"id":3240,"difficulty":208,"q":3241,"a":3242},"ref-latest-value","How do you read a \"live\" version of a prop or state inside a long-lived callback?","Keep a ref mirroring the latest value. The callback reads `ref.current` instead\nof the closed-over snapshot, so it always sees fresh data without being\nre-created.\n\n```jsx\nconst onTickRef = useRef(onTick)\nuseEffect(() => { onTickRef.current = onTick }) \u002F\u002F sync every render\n\nuseEffect(() => {\n  const id = setInterval(() => onTickRef.current(), 1000)\n  return () => clearInterval(id)\n}, []) \u002F\u002F interval created once; always calls the latest onTick\n```\n\nThis is the foundation of the `useEventCallback` pattern used in many hook\nlibraries to avoid re-creating timers, WebSockets, or subscriptions every time\na callback prop changes.\n",{"id":3244,"difficulty":121,"q":3245,"a":3246},"ref-module-var","How does a ref differ from a module-level variable?","A module-level `let` or `const` is **shared across all instances** of the\ncomponent. A ref is **per-instance** — each mounted copy of the component gets\nits own `ref.current`.\n\n```jsx\nlet moduleTimer = null \u002F\u002F shared by ALL Poller instances — bug!\n\nfunction Poller() {\n  const timerRef = useRef(null) \u002F\u002F each Poller has its own timer\n  \u002F\u002F ...\n}\n```\n\nUse refs for per-instance mutable values. Use module variables only for truly\nshared, singleton state (e.g., a global cache).\n",{"id":3248,"difficulty":121,"q":3249,"a":3250},"ref-conditional-element","What is ref.current before the element mounts or after it unmounts?","`ref.current` is `null` before mount and after unmount. React sets it to the DOM\nnode when the element is attached, then back to `null` when it's removed.\n\n```jsx\nuseEffect(() => {\n  \u002F\u002F safe: effect only runs after mount, ref.current is the DOM node\n  ref.current.focus()\n}, [])\n\n\u002F\u002F dangerous: reading ref.current during the first render body\nif (ref.current) ref.current.focus() \u002F\u002F null on first render — doesn't work\n```\n\nAlways access DOM refs inside effects or event handlers, never during render.\n",{"id":3252,"difficulty":121,"q":3253,"a":3254},"ref-no-rerender-caveat","What is the downside of using a ref instead of state?","Because mutating `ref.current` doesn't trigger a re-render, the UI won't update\nto reflect the new value. If you need to display or react to a value, it must be\nstate.\n\n```jsx\nconst ref = useRef(0)\nref.current++ \u002F\u002F counter increments, but nothing on screen changes\n\nconst [count, setCount] = useState(0)\nsetCount(c => c + 1) \u002F\u002F screen updates\n```\n\nA common mistake: using a ref to \"avoid renders\" for a value that's actually\ndisplayed in the JSX. The component won't visually update. Refs are for invisible\nbookkeeping.\n",{"id":3256,"difficulty":208,"q":3257,"a":3258},"ref-with-key","Why do refs get reset when a component's key changes?","Changing a `key` tells React to **unmount the old instance and mount a new one**.\nThe new instance starts with a fresh `useRef(null)` — the old ref's `.current`\nis set to `null` and the new instance creates its own ref.\n\n```jsx\n\u002F\u002F each time userId changes, the form unmounts and remounts\n\u003CUserForm key={userId} \u002F>\n\u002F\u002F any refs inside UserForm are re-created fresh\n```\n\nThis is expected and usually desirable — the `key` reset clears both state and\nrefs. If a parent holds a ref to the child's DOM node, the parent's ref is also\nreset after the remount.\n",{"id":3260,"difficulty":121,"q":3261,"a":3262},"forward-ref-function-component","Why can't you attach a ref directly to a function component?","Function components have no instance — there's nothing for `ref` to point at.\nReact will silently ignore the prop and `ref.current` stays `null`, which often\ncauses a confusing bug.\n\n```jsx\nconst MyInput = ({ value }) => \u003Cinput value={value} \u002F>\n\nconst ref = useRef(null)\n\u003CMyInput ref={ref} \u002F> \u002F\u002F ref.current is null — not the \u003Cinput>\n```\n\nThe fix is to wrap `MyInput` in `forwardRef` and pass the ref down to the DOM\nelement. Since React 19, `ref` is a plain prop on function components and\n`forwardRef` is no longer needed — but it's still needed in React 18 and below.\n",{"id":3264,"difficulty":96,"q":3265,"a":3266},"ref-vs-id","When should you use a ref instead of querying the DOM with getElementById?","Always prefer refs in React. `getElementById` queries the global document and\nbreaks if the same component is rendered multiple times or if IDs clash. A ref is\nscoped to the **specific DOM node** of this component instance.\n\n```jsx\n\u002F\u002F fragile: relies on a unique ID existing in the document\ndocument.getElementById('my-input').focus()\n\n\u002F\u002F correct: directly scoped to this component's node\nconst inputRef = useRef(null)\ninputRef.current?.focus()\nreturn \u003Cinput ref={inputRef} id=\"my-input\" \u002F>\n```\n\nRefs are also automatically cleaned up (set to `null`) when the element unmounts,\nunlike raw DOM queries that can hold stale references.\n",{"description":94},"React useRef interview questions — accessing DOM elements, mutable instance variables, forwardRef, useImperativeHandle, callback refs, and ref vs state.","react\u002Fhooks\u002Fuseref","useRef","eaOuY1ljO2ONFLi3HzWTSxFRa8gkwSk-FDT9pnLsvQo",{"id":3273,"title":3274,"body":3275,"description":94,"difficulty":121,"extension":97,"framework":10,"frameworkSlug":8,"meta":3279,"navigation":100,"order":73,"path":3280,"questions":3281,"questionsCount":1237,"related":169,"seo":3338,"seoDescription":3339,"stem":3340,"subtopic":3274,"topic":20,"topicSlug":21,"updated":174,"__hash__":3341},"qa\u002Freact\u002Fhooks\u002Fcustom-hooks.md","Custom Hooks",{"type":91,"value":3276,"toc":3277},[],{"title":94,"searchDepth":11,"depth":11,"links":3278},[],{},"\u002Freact\u002Fhooks\u002Fcustom-hooks",[3282,3286,3290,3294,3298,3302,3306,3310,3314,3318,3322,3326,3330,3334],{"id":3283,"difficulty":96,"q":3284,"a":3285},"what-is-custom-hook","What is a custom hook?","A custom hook is a **plain JavaScript function** whose name starts with `use` and\nthat calls one or more React hooks internally. It's the primary way to extract and\nshare **stateful logic** between components without changing their structure.\n\n```jsx\nfunction useWindowWidth() {\n  const [width, setWidth] = useState(window.innerWidth)\n  useEffect(() => {\n    const handleResize = () => setWidth(window.innerWidth)\n    window.addEventListener('resize', handleResize)\n    return () => window.removeEventListener('resize', handleResize)\n  }, [])\n  return width\n}\n\nfunction Banner() {\n  const width = useWindowWidth() \u002F\u002F any component can share this logic\n  return \u003Cdiv>{width > 768 ? 'desktop' : 'mobile'}\u003C\u002Fdiv>\n}\n```\n\nEach component that calls a custom hook gets its **own isolated state** — hooks\nare not singletons. Calling `useWindowWidth` in two components gives each its own\n`width` and `handleResize`.\n",{"id":3287,"difficulty":96,"q":3288,"a":3289},"naming-convention","Why must custom hooks start with \"use\"?","React's rules-of-hooks linter (`eslint-plugin-react-hooks`) and React itself use\nthe `use` prefix as the signal that a function *is* a hook and must follow the\nrules of hooks (called at the top level, not in conditions, etc.). Without `use`,\nthe lint rule won't check the function's internals for hook violations.\n\n```jsx\n\u002F\u002F not recognized as a hook — lint won't guard it\nfunction getUser() {\n  const [user] = useState(null) \u002F\u002F lint rule misses this\n  return user\n}\n\n\u002F\u002F recognized, fully linted\nfunction useUser() {\n  const [user] = useState(null)\n  return user\n}\n```\n\nThe `use` prefix is also a convention that communicates to other developers:\n\"this function uses hooks and must follow the rules.\"\n",{"id":3291,"difficulty":121,"q":3292,"a":3293},"custom-hook-vs-utility","What is the difference between a custom hook and a utility function?","A **utility function** is a plain function with no hooks — it can be called\nanywhere (inside or outside React). A **custom hook** calls at least one built-in\nor custom React hook, so it must follow the rules of hooks.\n\n```jsx\n\u002F\u002F utility: no hooks, usable anywhere\nfunction formatDate(date) {\n  return new Intl.DateTimeFormat('en-US').format(date)\n}\n\n\u002F\u002F custom hook: has state, must be called at the top level of a component\nfunction useFormattedDate(date) {\n  const { locale } = useContext(LocaleContext) \u002F\u002F uses a hook\n  return new Intl.DateTimeFormat(locale).format(date)\n}\n```\n\nIf you don't need React hooks, write a plain utility. Extract to a custom hook\nonly when the logic requires state, effects, context, or other hook functionality.\n",{"id":3295,"difficulty":96,"q":3296,"a":3297},"sharing-logic","How do custom hooks compare to HOCs and render props for sharing logic?","All three share stateful logic across components, but hooks are simpler:\n\n- **HOC** wraps a component, adds indirection, and can cause \"wrapper hell\" in\n  DevTools.\n- **Render props** avoid the wrapper issue but require restructuring JSX with a\n  callback function.\n- **Custom hook** is a plain function call — no extra component, no JSX change,\n  full TypeScript inference.\n\n```jsx\n\u002F\u002F HOC\nconst EnhancedComponent = withWindowWidth(MyComponent)\n\n\u002F\u002F render prop\n\u003CWindowWidth>{width => \u003CMyComponent width={width} \u002F>}\u003C\u002FWindowWidth>\n\n\u002F\u002F custom hook — simplest\nconst width = useWindowWidth()\n```\n\nCustom hooks are the idiomatic modern alternative and the reason HOCs\u002Frender\nprops have faded in React codebases.\n",{"id":3299,"difficulty":121,"q":3300,"a":3301},"use-previous","How do you build a usePrevious hook?","Store the previous value in a ref, syncing it to the latest value after each\nrender via an effect.\n\n```jsx\nfunction usePrevious(value) {\n  const ref = useRef()\n  useEffect(() => {\n    ref.current = value \u002F\u002F run after render, so ref still holds old value\n  })\n  return ref.current\n}\n\nfunction Counter() {\n  const [count, setCount] = useState(0)\n  const prevCount = usePrevious(count)\n  return \u003Cp>now: {count}, before: {prevCount}\u003C\u002Fp>\n}\n```\n\nThe key insight: `useEffect` (no deps) runs after every render. So during the\ncurrent render, `ref.current` still has the value from the **previous** render —\nexactly what the caller wants. The update in the effect fires after the return.\n",{"id":3303,"difficulty":121,"q":3304,"a":3305},"use-debounce","How do you build a useDebounce hook?","Maintain a debounced value in state; update it via a timeout that gets reset\nwhenever the raw value changes.\n\n```jsx\nfunction useDebounce(value, delay = 300) {\n  const [debounced, setDebounced] = useState(value)\n  useEffect(() => {\n    const id = setTimeout(() => setDebounced(value), delay)\n    return () => clearTimeout(id) \u002F\u002F reset on new value\n  }, [value, delay])\n  return debounced\n}\n\nfunction Search() {\n  const [query, setQuery] = useState('')\n  const debouncedQuery = useDebounce(query, 400)\n  \u002F\u002F run API call on debouncedQuery, not query\n}\n```\n\nThe cleanup function cancels the pending timeout on every keystroke, so\n`setDebounced` only fires after the user pauses for `delay` ms.\n",{"id":3307,"difficulty":121,"q":3308,"a":3309},"use-localstorage","How do you build a useLocalStorage hook?","Initialize state lazily from localStorage, then sync every update back to\nlocalStorage in an effect.\n\n```jsx\nfunction useLocalStorage(key, initial) {\n  const [value, setValue] = useState(() => {\n    try {\n      const stored = localStorage.getItem(key)\n      return stored !== null ? JSON.parse(stored) : initial\n    } catch {\n      return initial\n    }\n  })\n  useEffect(() => {\n    localStorage.setItem(key, JSON.stringify(value))\n  }, [key, value])\n  return [value, setValue]\n}\n\nconst [theme, setTheme] = useLocalStorage('theme', 'dark')\n```\n\nThe `try\u002Fcatch` handles JSON parse errors or SSR environments where\n`localStorage` doesn't exist. The lazy initializer reads storage once on mount.\n",{"id":3311,"difficulty":121,"q":3312,"a":3313},"use-fetch","How do you build a basic useFetch hook?","Encapsulate the loading\u002Ferror\u002Fdata state and the race-condition guard inside a\nhook that any component can call with a URL.\n\n```jsx\nfunction useFetch(url) {\n  const [state, setState] = useState({ data: null, loading: true, error: null })\n  useEffect(() => {\n    let active = true\n    setState({ data: null, loading: true, error: null })\n    fetch(url)\n      .then(r => r.json())\n      .then(data => { if (active) setState({ data, loading: false, error: null }) })\n      .catch(err => { if (active) setState({ data: null, loading: false, error: err }) })\n    return () => { active = false }\n  }, [url])\n  return state\n}\n```\n\nIn production, prefer a data library (React Query, SWR) that handles caching,\ndeduplication, and retries — but this pattern shows you know the pitfalls.\n",{"id":3315,"difficulty":121,"q":3316,"a":3317},"use-media-query","How do you build a useMediaQuery hook?","Use `window.matchMedia` to evaluate the query and subscribe to changes.\n\n```jsx\nfunction useMediaQuery(query) {\n  const [matches, setMatches] = useState(\n    () => window.matchMedia(query).matches\n  )\n  useEffect(() => {\n    const mq = window.matchMedia(query)\n    const handler = e => setMatches(e.matches)\n    mq.addEventListener('change', handler)\n    return () => mq.removeEventListener('change', handler)\n  }, [query])\n  return matches\n}\n\nconst isDesktop = useMediaQuery('(min-width: 1024px)')\n```\n\nGuard the initial state for SSR (where `window` doesn't exist) by checking\n`typeof window !== 'undefined'` or defaulting to `false`.\n",{"id":3319,"difficulty":121,"q":3320,"a":3321},"composing-hooks","Can custom hooks call other custom hooks?","Yes — and this is encouraged. Composing simple custom hooks into more complex ones\nmirrors how components compose, following the same mental model.\n\n```jsx\nfunction useUserPreferences() {\n  const [theme, setTheme] = useLocalStorage('theme', 'dark') \u002F\u002F custom hook\n  const isDesktop = useMediaQuery('(min-width: 1024px)')     \u002F\u002F custom hook\n  const { locale } = useContext(LocaleContext)\n  return { theme, setTheme, isDesktop, locale }\n}\n```\n\nEach layer stays focused and testable. The top-level hook just orchestrates the\npieces rather than reimplementing each concern.\n",{"id":3323,"difficulty":121,"q":3324,"a":3325},"when-to-extract","When should you extract logic into a custom hook?","Extract when: (1) the same hook combination appears in **two or more** components,\n(2) a component's hook logic is complex enough to obscure what the component\n**renders**, or (3) you want to test the logic independently of the rendering.\n\n```jsx\n\u002F\u002F if this pattern appears in three components, extract it\nconst [data, setData]     = useState(null)\nconst [loading, setLoading] = useState(false)\nconst [error, setError]   = useState(null)\n\u002F\u002F -> extract to useFetch(url)\n```\n\nDon't extract proactively. A hook with a single caller adds indirection without\nbenefit. Wait until duplication or complexity makes the extraction clearly worth\nthe added abstraction.\n",{"id":3327,"difficulty":121,"q":3328,"a":3329},"testing-custom-hook","How do you test a custom hook?","Use `@testing-library\u002Freact`'s `renderHook` utility, which mounts a minimal\ncomponent that calls your hook and returns its result.\n\n```jsx\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { useCounter } from '.\u002FuseCounter'\n\ntest('increments count', () => {\n  const { result } = renderHook(() => useCounter(0))\n  expect(result.current.count).toBe(0)\n  act(() => result.current.increment())\n  expect(result.current.count).toBe(1)\n})\n```\n\n`act` wraps state updates so the hook's state flushes before your assertions.\nWrap effects or async updates in `act` with `await` if they're async.\n",{"id":3331,"difficulty":96,"q":3332,"a":3333},"hook-return-shape","What can a custom hook return?","Anything — a single value, a tuple (like `useState`), an object, or nothing. The\nconvention is:\n\n- **Tuple** when the hook is analogous to `useState` and the caller will rename\n  the parts: `const [value, setValue] = useLocalStorage(key, init)`.\n- **Object** when there are many named exports and destructuring by name is\n  clearer: `const { data, loading, error } = useFetch(url)`.\n- **Single value** when there's only one thing to return: `const width = useWindowWidth()`.\n\n```jsx\n\u002F\u002F tuple: callers rename freely\nconst [open, setOpen] = useToggle(false)\n\n\u002F\u002F object: callers pick what they need\nconst { user, logout } = useAuth()\n```\n\nChoose based on readability at the call site, not based on what's \"correct.\"\n",{"id":3335,"difficulty":208,"q":3336,"a":3337},"stale-closure-custom-hook","How do stale closure bugs appear in custom hooks?","A custom hook captures props or parent state in closures just like inline hook\ncalls do. If a callback inside the hook isn't in the dependency array, it\ncloses over a stale value.\n\n```jsx\nfunction useInterval(callback, delay) {\n  useEffect(() => {\n    const id = setInterval(callback, delay) \u002F\u002F stale: callback changes but interval isn't reset\n    return () => clearInterval(id)\n  }, [delay]) \u002F\u002F missing `callback`\n}\n```\n\nThe fix: include `callback` in the dep array (causes interval teardown\u002Frecreate\non every render if callback isn't stable) or use the `ref` pattern — mirror the\ncallback into a ref each render and call `ref.current` inside the interval.\n\n```jsx\nfunction useInterval(callback, delay) {\n  const cbRef = useRef(callback)\n  useEffect(() => { cbRef.current = callback }) \u002F\u002F always current\n  useEffect(() => {\n    const id = setInterval(() => cbRef.current(), delay)\n    return () => clearInterval(id)\n  }, [delay])\n}\n```\n",{"description":94},"React custom hooks interview questions — building your own hooks, naming conventions, composing hooks, usePrevious, useDebounce, useLocalStorage, and testing patterns.","react\u002Fhooks\u002Fcustom-hooks","79ElYQItZoGOQ8LHERFD37rm-3y2afQZJ2HQBfanTkY",1782244096585]