skip to Main Content

I know how to send data from child to parent component when a button is clicked, but the button should be in child component.
What I want is, how to send data from child to parent when a button is clicked, but the button is in parent component not in child component.

For instance, I have something like this:

Parent component

function ParentComponent() {
  const handleChildData = (childData) => {
    console.log(childData);
  }
  return (
    <> 
    <ChildComponent onDataGet={handleChildData} />
    <div>ParentComponent</div>
    </>
  )
}

Child component

function ChildComponent({onDataGet}) {
  const data = {
    firstName: "John",
    lastName: "Smith",
  }

  // Based on something, execute this function:
  onDataGet(data)

  return (
    <div>ChildComponent</div>
  )
}

I have tried this approach:

defined a state, on each click the state increments by one, also passed this state to child, in there anytime the state gets changed, useEffect in child component will happen and will execute the onDataGet function. Like this:

Parent Component

function ParentComponent() {
  const [isTrigerred, setIsTrigerred] = useState(1);
  const handleChildData = (childData) => {
    console.log(childData);
  };
  return (
    <>
      <ChildComponent onDataGet={handleChildData} gotChanged={isTrigerred} />
      <div>ParentComponent</div>
      <Button variant="contained" onClick={() => setIsTrigerred((prev) => prev + 1)}>
        Click Me
      </Button>
    </>
  );
}

Child Component

function ChildComponent({ onDataGet, gotChanged}) {
  const data = {
    firstName: "John",
    lastName: "Smith",
  };

  useEffect(() => {
    // Don't execute in first render
    if (gotChanged !== 1) {
      onDataGet(data);
    }
  }, [gotChanged]);

  return <div>ChildComponent</div>;
}

But I’m looking for a better approach, if there is any.

Thanks.

2

Answers


  1. One approach may be to pass a setter for the wanted function in your child component like this :

    Parent Component :

    function ParentComponent() {
      const [getChildData, setGetChildData] = useState(() => null);
    
      const handleChildData = (childData) => {
        const childData = getChildData();
        // etc...
      };
      return (
        <>
          <ChildComponent getDataSetter={setGetChildData} />
          <div>ParentComponent</div>
          <Button variant="contained" onClick={handleChildData}>
            Click Me
          </Button>
        </>
      );
    }
    

    Child Component :

    function ChildComponent({ getDataSetter }) {
      const data = {
        firstName: 'John',
        lastName: 'Smith',
      };
    
      useEffect(() => {
        // Only way to use a state for a function, because the setter function
        // can take a callback to set the new value
        getDataSetter(() => () => data);
      }, [data]);
    
      return <div>ChildComponent</div>;
    }
    
    
    Login or Signup to reply.
  2. The caching function of ref can be used to realize the method of parent calling child components.

    import {useRef} from 'react'
    
    function ParentComponent() {
      const actionRef = useRef();
      return (
        <>
          <ChildComponent ref={actionRef} onDataGet={handleChildData} />
          <div>ParentComponent</div>
          <Button
            variant="contained"
            onClick={() => {
              const childData = actionRef.current.getData();
              console.log('childData...', childData);
            }}
          >
            Click Me
          </Button>
        </>
      );
    }
    
    import {forwardRef} from 'react'
    
    forwardRef(function ChildComponent(props, ref) {
      const data = {
        firstName: 'John',
        lastName: 'Smith',
      };
    
      useImperativeHandle(
        ref,
        () => {
         // the return object will pass to parent ref.current, so you can add anything what you want.
          return {
            getData: () => data,
          }
        },
        [data],
      );
    
      return <div>ChildComponent</div>;
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search