AmCurious
AmCurious

Reputation: 137

Trigger child function from parent component using react hooks

I have some action buttons in parent components. On click of one of such buttons, I would like to trigger a function in the child component. Currently, I am trying to implement it using useRef hook. But the solution seems tedious and also gives me warning:

enter image description here

My current code looks like:

import React, {useContext, useEffect, useState, useRef} from 'react';
const ParentComponent = ({...props})=> {
const myRef = useRef();
const onClickFunction = () => {
        if(myRef.current) {
            myRef.current.childFunction();
        }
    }
return (
<ChildComponent ref = {myRef}/>
);
}

Child component

const ChildComponent = (({}, ref,{  actionButtons, ...props}) => {
const [childDataApi, setChildDataApi] = useState(null);

const childFunction = () => {
       //update childDataApi and pass it to parent
        console.log("inside refreshEntireGrid");
    }
});

Firstly, is there a better solution then trying to trigger childFunction from parent ? For this I am following this solution: Can't access child function from parent function with React Hooks I tried adding forward ref but that threw error as well. enter image description here

I also found out that lifting the state up could be another solution as well. But I am not able to understand how to apply that solution in my case. Can someone please help me with this.

Upvotes: 10

Views: 25651

Answers (3)

Menzi
Menzi

Reputation: 365

One way to call a child component's function from its parent is with the useRef hook

const Parent = () => {
  const childFunc = React.useRef(null)

  return (
    <>
      <Child childFunc={childFunc} />
      <button onClick={() => childFunc.current()}>Click me</button>
    </>
  )
}

const Child = ({ childFunc }) => {
  React.useEffect(() => {
    childFunc.current = alertUser
  }, [childFunc.current])

  function alertUser() {
    alert('You clicked!')
  }

  return null
}

render(<Parent />)

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 203542

The warning says you were using forwardRef so with your snippet const ChildComponent = (({}, ref, { actionButtons, ...props }) => { .... } I'll assume this is a typo in your question and you were actually doing const ChildComponent = React.forwardRef(({}, ref,{ actionButtons, ...props }) => { .... }).

The issue here, and the warning message points this out, is that you are passing a third argument to forwardRef when it only consumes two. It seems you destructure nothing from the first props argument. From what I can tell you should replace the first argument with the third where it looks like you are doing some props destructuring.

const ChildComponent = React.forwardRef(({ actionButtons, ...props }, ref) => { .... }

From here you should implement the useImperativeHandle hook to expose out the function from the child.

const ChildComponent = React.forwardRef(({ actionButtons, ...props }, ref) => {
  const [childDataApi, setChildDataApi] = useState(null);
  
  const childFunction = () => {
    // update childDataApi and pass it to parent
    console.log("inside refreshEntireGrid");
  }

  useImperativeHandle(ref, () => ({
    childFunction
  }));

  ...

  return ( ... );
});

In the parent component:

const ParentComponent = (props) => {
  const myRef = useRef();

  const onClickFunction = () => {
    myRef.current?.childFunction();
  }

  return (
    <ChildComponent ref={myRef}/>
  );
}

Upvotes: 22

squillman
squillman

Reputation: 13641

Something else you can try is to pass a prop to the child to indicate that the button has been clicked and use useEffect in the child component to do something when that value changes.

const Child = props => {
  useEffect(() => TriggeredFunc(), [props.buttonClicked]);

  const TriggeredFunc = () => {
    ...
  }

  return '...';
}

const Parent = () => {
  const [buttonClicked, setButtonClicked] = useState(0);

  const onClick = e => {
    setButtonClicked(buttonClicked++);
  }

  return <>
    <button onClick={onClick}>My Button</button>
    <Child buttonClicked={buttonClicked} />;
  </>

}

Upvotes: 10

Related Questions