David Johns
David Johns

Reputation: 1734

How to fire a function in child from parent in react?

My React app has three components. Two of them are child components and the other is parent. I need to pass a data (projectId) from one child component to the other child through the parent component and after receiving the data, fire a function. As my example, I'm sending projectId from ChildOne to Parent and then send projectId from Parent to ChildTwo. ChildTwo has a function called setProject(projectId) and I need to fire it once the projectID is received. The problem is I can't get the function getProjectId fired in ChildTwo by clicking on the button in ChildOne. I also tried with componentDidMount and componentWillReceiveProps which are not working for me. How can I do this?

Here what I tried

ChildOne :

class ChildOne extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
          projectId: 3,
        };
    }

    sendProjectId = (projectId) => {
       this.props.sendId(projectId)
    }

    render() {
       return(
         <button onClick={() => this.sendProjectId(this.state.projectId)}>
            Click
         </button>
       )
    }
}

Parent:

class Parent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
          projectId: '',
        };
    }

    getId = (proId) => {
       this.setState({
          projectId : proId
       })
    }

    render() {
       return(
         <div>
           <CildOne sendId={this.getId} />

           <CildTwo sendOneId={this.state.projectId} />
         </div>
       )
    }
}

ChildTwo:

class ChildTwo extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
          projectId: '',
        };
    }

    getProjectId = (this.props.sendOneId) => {
       //Do something with this.props.sendOneId
    }

    render() {
       return(
         <div></div>
       )
    }
}

Upvotes: 3

Views: 923

Answers (4)

German
German

Reputation: 557

Our you can try a functional component or hooks if you want to set some state

function ChildOne(props) {
  const [projectId, setProjectId] = useState(3);
  function sendProjectId(data){
       props.sendId(projectId)
    }
   return(
         <button onClick={() => sendProjectId(projectId)}>
            Click
         </button>
       )
}



function ChildTwo(props) {
  const [state, setState] = useState('')
  function getProjectId(data) {
       //Do something with this.props.sendOneId
       console.log(`data here ${data}`)
       return false;
    }
    getProjectId(props.sendOneId)
  return (
    <div>
    </div>
  )
}


function Parent(){
   const [projectId, setProjectId] = useState('');
   function getId(proId) {
     setProjectId(proId)
   }
   return(
         <div>
           <ChildOne sendId={getId} />

           <ChildTwo sendOneId={projectId} />
         </div>
       )
}



Upvotes: 1

Andy
Andy

Reputation: 63587

You should probably use componentDidUpdate with a condition to check to see whether the projectId in state needs to be updated when sendOneId changes. You can then use setStates callback to call getProjectId:

componentDidUpdate() {
  const { projectId: currentProjectId } = this.state;
  const { sendOneId: projectId } = this.props;
  if (projectId !== currentProjectId) {
    this.setState({ projectId }, () => this.getProjectId());
  }
}

Full working example:

class ChildOne extends React.Component {
    
  constructor(props) {
    super(props);
      this.state = {
        projectId: 3,
      };
    }

    sendProjectId = (projectId) => {
      this.props.sendId(projectId)
    }

    render() {
      return (
        <button onClick={() => this.sendProjectId(this.state.projectId)}>
          Click
        </button>
      );
    }
}

class Parent extends React.Component {
  
  constructor(props) {
    super(props);
    this.state = {
      projectId: '',
    };
  }

  getId = (projectId) => {
    this.setState({ projectId });
  }

  render() {
    return (
      <div>
        <ChildOne sendId={this.getId} />
        <ChildTwo sendOneId={this.state.projectId} />
      </div>
    )
  }

}

class ChildTwo extends React.Component {

    constructor(props) {
      super(props);
      this.state = {
        projectId: '',
      };
    }

    componentDidUpdate() {
      const { projectId: currentProjectId } = this.state;
      const { sendOneId: projectId } = this.props;
      if (projectId !== currentProjectId) {
        this.setState({ projectId }, () => this.getProjectId());
      }
    }

    getProjectId = () => {
      console.log(this.state.projectId);
    }

    render() {
      return (
        <div></div>
      );
    }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container"></div>

Upvotes: 1

Vishal
Vishal

Reputation: 6378

You did a mistake in getProjectId function of ChildTwo component.

Your function cannot receive anything as a parameter from prop.

So, your function should look like:

getProjectId = (sendOneId) => {
   //Do something with this.props.sendOneId
}

Then you should use componentWillReceiveProps like this:

componentWillReceiveProps(nextProps) {
  if (this.props.sendOneId !== nextProps.sendOneId) {
    this.getProjectId(nextProps.sendOneId);
  }
}

Here is a working codesandbox example that I created to fix your problem: https://codesandbox.io/s/5v4rn7qnll

Upvotes: 1

Agney
Agney

Reputation: 19224

This would depend on what ChildTwo wants to accomplish with the said data.

Case 1:

ChildTwo intends to fetch some data with the corresponding projectId and display it in the component. Then, you can easily fetch this data in the parent component and pass the data down as props.

class Parent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
          projectId: '',
          dataForChildTwo: null,
        };
    }

    getId = (proId) => {
       this.setState({
          projectId : proId,
          dataForChildTwo: fetchData(proId)
       })
    }

    render() {
       return(
         <div>
           <CildOne sendId={this.getId} />

           <CildTwo data={this.state.dataForChildTwo} />
         </div>
       )
    }
}

Case 2:

ChildTwo intends to make some change to something inside it when projectId changes. Then you can use componentDidUpdate hook to see if prop changed and respond to it.

class ChildTwo extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
          projectId: '',
        };
    }

    getProjectId = (this.props.sendOneId) => {
       //Do something with this.props.sendOneId
    }

    componentDidUpdate(prevProps) {
      if(this.props.projectId!==prevProps.projectId) {
        // do something
      }
    }

    render() {
       return(
         <div></div>
       )
    }
}

Case 3:

If none of the above cases work for you, then you can manually reload the complete component when the projectId changes using a key attribute:

<CildTwo key={this.state.projectId} sendOneId={this.state.projectId} />

Note: This reloads the whole component quite unnecessarily.

Upvotes: 3

Related Questions