useImpertativeHandle — React Hook

AkashSDas
2 min readMar 28, 2024

--

The useImperativeHandle hook in allows a component to customize the instance value that is exposed to parent components when using ref.

useImperativeHandle is particularly useful when we want to hide some internal implementation details of a component and only expose a specific subset of its functionality through the ref.

Following is its signature:

useImperativeHandle(ref, createHandle, dependencies?)

Here, ref is the reference of component that we’ve received as the second argument from the forwardRef render function. createHandle is a function that takes no arguments and returns the ref handle we want to expose. That ref handle can have any type. Usually, we’ll return an object with the methods we want to expose.

dependencies (optional) is a list of all reactive values, used inside the setup function. Reactive values include props, state, and all the variables and functions declared directly inside our component body. React will compare each dependency with its previous value using the Object.is comparison.

Following is an example of simple usage of useImperativeHandle hook on a component (Post). This will take care of exposing focus on input element and scrolling behavior.

import { useRef, useImperativeHandle, forwardRef } from "react";

type CommentListRef = {
scrollToBottom: () => void;
};

type PostRef = {
scrollAndFocusAddComment: () => void;
};

export default function App(): JSX.Element {
const postRef = useRef<PostRef>(null);

function handleClick(): void {
postRef.current?.scrollAndFocusAddComment();
}

return (
<div>
<button onClick={handleClick}>Write a comment</button>
<Post ref={postRef} />
</div>
);
}

const Post = forwardRef<PostRef>(function Post(_props, ref) {
const commentsRef = useRef<CommentListRef>(null);
const addCommentRef = useRef<HTMLInputElement>(null);

useImperativeHandle(
ref,
function () {
return {
scrollAndFocusAddComment() {
commentsRef.current?.scrollToBottom();
addCommentRef.current?.focus();
},
};
},
[]
);

return (
<>
<CommentList ref={commentsRef} />
<AddCommentInput ref={addCommentRef} />
</>
);
});

const CommentList = forwardRef<CommentListRef>(function CommentList(
_props,
ref
) {
const divRef = useRef<HTMLDivElement>(null);

const comments: JSX.Element[] = [];
for (let i = 0; i < 100; i++) {
comments.push(<p key={i}>#{i + 1} Comment</p>);
}

useImperativeHandle(ref, function () {
return {
scrollToBottom: () => {
divRef.current?.scrollIntoView({ behavior: "smooth" });
},
};
});

return <div ref={divRef}>{comments}</div>;
});

const AddCommentInput = forwardRef<HTMLInputElement>(function AddCommentInput(
props,
ref
) {
return <input {...props} ref={ref} />;
});

We shouldn’t overuse refs. We should only on useRef for imperative behaviors that can’t express as props (eg: scrolling to a node, focusing a node, triggering an animation, selecting text, and so on).

If we can express something as a prop, we shouldn’t use a ref. For example, instead of exposing an imperative handle like { open, close } from a Modal component, it is better to take isOpen as a prop like <Modal isOpen={isOpen} />. Effects can help us expose imperative behaviors via props.

--

--