In React, how to invoke a method of a child component from the parent component when both of them are functional components?

NISARG SHAH
3 min readAug 30, 2019

So a few days ago, I started working with React. And, decided to go with function components (rather than class components) as suggested by most people online.

So I started with the online tutorial on React’s site, and other documentation, it was mostly enough to get going in a few hours. But after a short while I found myself stuck on this question:

How do I trigger an action in a child component from a parent component?

This is somewhat easy with the class components. You just need to declare a ref and then you could call functions like a normal Javascript instance.

For example, let’s start with a simple counter which is introduced in React’s documentation of Hooks:

class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}

Notice that this is a class component. Now let’s try to make a wrapping component, which has a reset button — which should be able to reset the count to zero.

I know, I know, you are supposed to Lift The State Up in such cases, and pull the count variable to the containing component. But let’s say we need the counter to take responsibility for its state, and we also need to reset it from ‘outside’.

Back to the example: In order to be able to invoke a method on the child component, we need to declare a ref in the markup — in order to access the “reference” of the “instance” of the child component. Since we are working with class component, it has an instance that we can reference to.

class Container extends React.Component  {
constructor(props) {
super(props);
this.counter = React.createRef();
}

resetCount() {
this.counter.current.resetCount();
}

render() {
return (<div>
<button onClick={() => this.resetCount()}>Reset Count</button>
<br/>
<Counter ref={this.counter} />
</div>);
}
}
class Counter extends React.Component {
constructor(props) {
...
}

resetCount() {
this.setState({
count: 0
});
}
render() {
...
}
}

So basically we have three major changes:

  1. Now there is a <Container /> element wrapping the <Counter />
  2. In the markup of <Counter /> we use a ref={this.counter} to capture the reference of Counter instance.
  3. We have declared the counter variable in the constructor as follows: this.counter = React.createRef();.
  4. While handling the click event, we are calling resetCount function from the instance of child component through this.counter.current.resetCount().

And that’s it! We can now call any function of the child component from the parent component! It is just as simple to do so in a function component, but finding “how to do it” was the hardest part.

Again, let’s start with the counter, implemented with a function component this time.

const Counter = () => {
const [count, setCount] = useState(0);

return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

Now we need 3 small changes to this code in order to allow the container to invoke a function of counter. First, let’s see the finished code:

const { useState, useRef, useImperativeHandle, forwardRef } = React;const Counter = forwardRef((props, ref) => {
const [count, setCount] = useState(0);

useImperativeHandle(ref, () => ({
reset: () => {
setCount(0);
}
}), []);
return (
...
);
});
const Container = () => {
const counter = useRef(null);

const resetCounter = () => {
counter.current.reset();
}

return (<div>
<button onClick={resetCounter}>Reset</button>
<Counter ref={counter} />
</div>);
}

Here are the changes you need to make:

  1. In the container component, we use ref declaration to assign the ref to a counter variable. Also notice that we have declared counter variable using useRef hook.
  2. The other hook we need to use is useImperativeHandle, combined with forwardRef. This allows us to handle how the reference of the child component behaves. Here we return an object with “reset” function as a property, that allows the parent component to mutate the state of counter.
  3. Finally, in the parent component, we invoke the reset function of counter, in the click event handler of the “Reset” button.

That’s it. Pretty easy, right? Hope it helped!

--

--