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 usingref
. 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.