How to call child component method with forwardRef and useImperativeHandle in React

Calling a child method is a common architecture problem that occurs in a React app. You can pass individual properties to child components, you can alter the component state inside of the same component, but how to actually call another component’s method?

There’s a way to do that by using a combination of forwardRef with useImperativeHandle.

What exactly are those functions for? Let’s quote the documentation:

  • forwardRef lets your component expose a DOM node to parent component with a ref.
  • useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases.

So, we need to do both things – first, wrap a component with a forwardRef, then useImperativeHandle to fake the ref the component returns. I’m going to throw together a simple Button component:

import React, { forwardRef, useImperativeHandle, useRef } from 'react';

let buttonIndex = 0;

const ChildButton = forwardRef((props, ref) => {
  ++buttonIndex;

  const index = buttonIndex;

  useImperativeHandle(ref, () => ({
    showMe() {
      let div = document.createElement('div');
      div.innerText = index;
      document.getElementById('result').appendChild(div);
    }
  }));

  return <button style={{ backgroundColor: '#4594ff', border: 'none', fontSize: '16px', padding: '10px', cursor: 'pointer' }}>{props.text}</button>
})

With this, the component is ready to receive a ref and will return the object we provide as a second parameter in useImperativeHandle. Now, the main component just needs to provide a ref, and it will be able to call the child methods through the ref variable:

const App = function() {
  const ref1 = useRef();
  const ref2 = useRef();
  const ref3 = useRef();

  return <div>
    <div style={{ gap: 8, display: 'flex' }}>
      <span onClick={() => { ref1.current.showMe(); }}>
        <ChildButton text="Button 1" ref={ref1} />
      </span>
      <span onClick={() => { ref2.current.showMe(); }}>
        <ChildButton text="Button 2" ref={ref2} />
      </span>
      <span onClick={() => { ref3.current.showMe(); }}>
        <ChildButton text="Button 3" ref={ref3} />
      </span>
    </div>
    <div id="result" style={{ fontSize: '16px' }}></div>
  </div>
}

And here’s the result!


Did you know that I made an in-browser image converter, where you can convert your images without uploading them anywhere? Try it out and let me know what you think. It's free.

Leave a Comment

Your email address will not be published. Required fields are marked *