Search code examples
reactjsreact-redux

How to call a child method on parent events with react?


I have a child component that need to listen to one of its parent event. More precisely, I have a function in the child component that takes as parameter an event from the parent. I would like to call this function everytime the event occurs.

As an example, here is a code snippet:

class Parent extends React.Component {
  handleKeyDown = (event) => {
    // Call the child function doSomething()
  }

  render() {
    return (
      <input
        type="text"
        onKeyDown={this.handleKeyDown}
      >

      <Child />
    )
  }
}
class Child extends React.Component {
  doSomething = (event) => {
    // Get the event from parent
  }

  render() {
    return (
      ...
    )
  }
}

I have considered two ways to do it:

  • Using ref to call the child function from the parent onKeyDown
  • Using a state to store the event and pass it as a props to the child, then listen to props changes with getDerivedStateFromProps.

However, none of these solutions seems very appealing. I have also thought about using a redux function but I need data from the child component as well as the event from the parent component... I was wondering if there is a clean way do to that?


Solution

  • Update (hooks):

    I updated my components to use hooks and ended up using useRef(), useImperativeHandle() and forwardRef() (the latter only for React < 19) to handle this case:

    const Parent = () => {
      const childRef = useRef();
    
      const handleKeyDown = (event) => {
        // Call the child function doSomething()
        childRef.current.doSomething(event);
      };
       
      return (
        <input
          type="text"
          onKeyDown={handleKeyDown}
        >
        
        <Child ref={childRef} />
      );
    };
    
    // React < 19
    
    const Child = forwardRef((props, ref) => {
      useImperativeHandle(ref, () => ({
        doSomething: (event) => {
          // Get the event from parent
        }
      }));
    
      return (
        [...]
      );
    });
    
    Child.displayName = "Child";
    
    // React >= 19
    
    const Child = ({ ref, ...props }) => {
      useImperativeHandle(ref, () => ({
        doSomething: (event) => {
          // Get the event from parent
        }
      }));
    
      return (
        [...]
      );
    };
    

    Original answer (class component):

    I decided to use the solution provided by Francis Malloch on this post1:

    class Parent extends React.Component {
      childCallables = null;
        
      setChildCallables = (callables) => {
        this.childCallables = callables;
      }
        
      handleKeyDown = (event) => {
        // Call the child function doSomething()
        this.childCallables.doSomething(event);
      }
        
      render() {
        return (
          <input
            type="text"
            onKeyDown={this.handleKeyDown}
          >
        
          <Child setCallables={this.setChildCallables} />
        )
      }
    }
    
    class Child extends React.Component {
      componentDidMount() {
        this.props.setCallables({
          doSomething: this.doSomething
        });
      }
        
      doSomething = (event) => {
        // Get the event from parent
      }
        
      render() {
        return (
          [...]
        )
      }
    }
    

    Basically, I'm using a props to store the child's methods I need to access from the parent. The methods are saved in the props just after the child component is mounted.


    1. Since it is an answer to a completely different question, I don't think marking this one as a duplicate would make sense.