Hint
useImperativeHandle prevents parents from directly accessing internal DOM nodes or state. Expose only the API you want to maintain — it's the ref equivalent of keeping implementation details private.
// Simulates useImperativeHandle
function createImperativeHandle(ref, factory) {
ref.current = factory();
}
// Child exposes ONLY what it wants
function VideoPlayer(ref) {
const internal = {
play() { return 'playing'; },
pause() { return 'paused'; },
seek(t) { return 'seeked to ' + t; },
_internalState: 'hidden', // should NOT be exposed
};
createImperativeHandle(ref, () => ({
play: () => internal.play(),
pause: () => internal.pause(),
// seek and _internalState NOT exposed
}));
}
const playerRef = { current: null };
VideoPlayer(playerRef);
console.log(typeof playerRef.current.play);
console.log(typeof playerRef.current.seek);
console.log(playerRef.current._internalState);
console.log(playerRef.current.play());function undefined undefined playing
Explanation: useImperativeHandle replaces ref.current with a custom object. Only play and pause are exposed. seek and _internalState are hidden.
Key Insight: useImperativeHandle prevents parents from directly accessing internal DOM nodes or state. Expose only the API you want to maintain — it's the ref equivalent of keeping implementation details private.