Lesson 30 of 80
A ref placed on a custom component doesn't just work — a plain function component has no instance for React to attach it to, and forwardRef is exactly the plumbing that fixes that.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 2
What does useImperativeHandle let you do that plain forwardRef alone doesn't?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
<input ref={myRef} /> works because input is a real DOM element with a node for React to hand myRef.current. <CustomInput ref={myRef} /> does not work the same way by default — a function component has no instance, so there's nothing to attach the ref to, and ref isn't even passed through as a normal prop.
forwardRef is the explicit opt-in that lets a custom component receive a ref and decide where it points:
const CustomInput = forwardRef(function CustomInput(props, ref) {
return <input {...props} ref={ref} />; // forward it to the real DOM node inside
});
<CustomInput ref={myRef} /> // myRef.current is now the actual <input> DOM node
<CustomInput ref={myRef} /> — ref isn't passed as a normal prop. On a plain function component, it does nothing.
const CustomInput = forwardRef(function CustomInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ""; },
}));
return <input {...props} ref={inputRef} />;
});
// myRef.current is now { focus, clear } — not the raw DOM node
forwardRef, passing ref to a custom component either does nothing or logs a warning — ref is deliberately excluded from props in a plain function component; it can only be received through the second argument forwardRef provides.useImperativeHandle's exposed object is only recomputed based on its own dependency array (the third, optional argument) — forgetting a dependency there causes the same stale-closure risk as any other hook with a dependency array."How would you let a parent component call .focus() on a custom input?" is a direct, practical test of forwardRef/useImperativeHandle — and recognizing when not to reach for this (most of the time) is just as much a part of a strong answer as knowing the API.