All posts

React useImperativeHandle: Let the parent trigger functions of a child component

useImperativeHandle is the magic word. More about it later!

In React, the way of communicating between parent and child components is usually done in 2 ways:

Classic ways

An example of using props:

// Parent component
const MyPage = () => {
  return <Modal open={true} title="Submit your score" />;
};

// Child component
const Modal = (props: ModalProps) => {
  const { open, title } = props;

  if (!open) {
    return null;
  }

  return (
    <div className="modal">
      <h3>{title}</h3>
      ...
    </div>
  );
};

And an example of using a callback function:

// Parent component
const MyPage = () => {
  const [isOpen, setIsOpen] = useState<boolean>(false);

  return (
    <Modal
      open={isOpen}
      title="Submit your score"
      // This is the callback function
      toggleOpen={() => setIsOpen(!isOpen)}
    />
  );
};

// Child component
const Modal = (props: ModalProps) => {
  const { open, title, toggleOpen } = props;

  if (!open) {
    return null;
  }

  return (
    <div className="modal">
      <button onClick={toggleOpen}>Close</button>
      <h3>{title}</h3>
      ...
    </div>
  );
};

useImperativeHandle

Sometimes you encounter a situation where you have functions in the child component, that need to be called from a parent component. For example I recently encountered this when creating the react-drawio package.

The package I created contained a few functions to interact with the embedded iframe. I wanted to expose those functions to any component that would implement the react-drawio package.

I was looking for a solution when I stumbled upon the React useImperativeHandle hook. The React docs describe the hook like this: useImperativeHandle is a React Hook that lets you customize the handle exposed as a ref.

Let me break that down a bit.

You have probably used useRef before to get access to the API of a certain DOM element. For example to put .focus() on an element, or to get a certain attribute from an element by using getAttribute().

That's all good, but did you know you can use useRef for accessing the API of a React component?

The magic

// Parent component
const MyPage = () => {
  const modalRef = useRef<ModalRef>(null);

  useEffect(() => {
    if (modalRef.current) {
      drawioRef.current.myExtraFunction();
    }
  }, [modalRef.current]);

  return (
    <Modal ref={modalRef} />
  );
};

// Child component
const Modal = forwardRef<ModalRef, ModalProps>((props, ref) => {
  const focus = () => {
      // Do something
  };

  const myExtraFunction = () => {
      // Do something
  };

  useImperativeHandle(ref, () => ({
    focus,
    myExtraFunction
  }), []);  

  return (
    <div className="modal">
      ...
    </div>
  );
});

Two things to notice here.

  1. I wrapped the Modal component in a React forwardRef function. React docs describe this function as forwardRef lets your component receive a ref and forward it to a child component.
  2. I used useRef in the parent component which can now access both focus and myExtraFunction from the child component, because I only exposed those 2 functions.

This is not a replacement for props or callback functions, and you might not even need it that often, but just know that it's there!

Let me know if you found this useful, or if you're already using it for other use cases!

Published Aug 29, 2023

ReacthooksuseImperativeHandle
Dev stuff by Marc Veens

Marc Veens

Stories about my adventures in the wondrous world of front-end development.