[{"data":1,"prerenderedAt":135},["ShallowReactive",2],{"qa-\u002Freact\u002Fpatterns\u002Fforward-ref-imperative":3},{"page":4,"siblings":115,"blog":132},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":105,"related":106,"seo":107,"seoDescription":108,"stem":109,"subtopic":110,"topic":111,"topicSlug":112,"updated":113,"__hash__":114},"qa\u002Freact\u002Fpatterns\u002Fforward-ref-imperative.md","Forward Ref Imperative",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","React","react",{},true,5,"\u002Freact\u002Fpatterns\u002Fforward-ref-imperative",[23,28,32,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101],{"id":24,"difficulty":25,"q":26,"a":27},"why-no-ref-on-function-components","easy","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":29,"difficulty":25,"q":30,"a":31},"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":33,"difficulty":34,"q":35,"a":36},"what-is-use-imperative-handle","medium","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":38,"difficulty":34,"q":39,"a":40},"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":42,"difficulty":34,"q":43,"a":44},"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":46,"difficulty":34,"q":47,"a":48},"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":50,"difficulty":14,"q":51,"a":52},"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":54,"difficulty":14,"q":55,"a":56},"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":58,"difficulty":14,"q":59,"a":60},"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":62,"difficulty":34,"q":63,"a":64},"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":66,"difficulty":34,"q":67,"a":68},"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":70,"difficulty":25,"q":71,"a":72},"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":74,"difficulty":14,"q":75,"a":76},"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":78,"difficulty":14,"q":79,"a":80},"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":82,"difficulty":34,"q":83,"a":84},"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":86,"difficulty":34,"q":87,"a":88},"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":90,"difficulty":14,"q":91,"a":92},"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":94,"difficulty":34,"q":95,"a":96},"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":98,"difficulty":25,"q":99,"a":100},"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":102,"difficulty":34,"q":103,"a":104},"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",20,null,{"description":11},"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","Patterns","patterns","2026-06-24","3l-TC_wGYc6rODTadiYbBCLWtDVvE0FvoJi49qa5mBg",[116,120,123,127,131],{"subtopic":117,"path":118,"order":119},"Compound Components","\u002Freact\u002Fpatterns\u002Fcompound-components",1,{"subtopic":121,"path":122,"order":12},"Render Props & HOCs","\u002Freact\u002Fpatterns\u002Frender-props-hoc",{"subtopic":124,"path":125,"order":126},"Error Boundaries","\u002Freact\u002Fpatterns\u002Ferror-boundaries",3,{"subtopic":128,"path":129,"order":130},"Portals & Refs","\u002Freact\u002Fpatterns\u002Fportals-refs",4,{"subtopic":110,"path":21,"order":20},{"path":133,"title":134},"\u002Fblog\u002Freact-forward-ref-imperative-guide","React forwardRef & useImperativeHandle — Complete Interview Guide",1782244101306]