'How can I trigger a method in a subcomponent when parent changes props?
I have a React web application that has three components, a parent and two child subcomponents. I've left out the JavaScript related to <HeaderComponent /> because it isn't relevant to this question.
class App extends Component {
token = null;
constructor(props) {
super(props);
this.state = {
AllInvoices: [],
CurrentInvoice: null
};
this.setCurrentInvoice = this.setCurrentInvoice.bind(this);
}
setCurrentInvoice = (sharedValue) => {
this.setState({
CurrentInvoice: sharedValue
});
}
componentDidMount()
{
fetch("http://api.app.local/api/getALLinvoices", {
"method": "GET"
})
.then(resp => {
this.setState({
AllInvoices: resp.Result
CurrentInvoice: resp.Result[0]
});
});
}
componentDidUpdate()
{
// this DOES trigger
console.log("App componentDidUpdate triggered");
}
render() {
return (
<>
<HeaderComponent AllInvoices={this.state.AllInvoices} setCurrentInvoice={this.setCurrentInvoice} />
<MainFormComponent CurrentInvoice={this.state.CurrentInvoice} />
</>
)
}
}
class MainFormComponent extends Component {
token = null;
constructor(props) {
super(props);
this.state = {
Field1Value: "",
Field2Value: "",
Field3Value: "",
// ...
Field12Value: ""
};
}
componentDidUpdate()
{
//> For some reason this does NOT trigger when CurrentInvoice is updated
console.log("MainFormComponent componentDidUpdate triggered");
}
getInvoiceDetailsAndUpdateForm = () =>
{
fetch("http://api.app.local/api/getinvoicedetails", {
"method": "POST",
"body": {
"invoice_id": this.props.CurrentInvoice.Id
}
})
.then(resp => {
/*
* Run various business logic and update 12+ form fields with AJAX response
*
*/
});
}
render() {
return (
<>
<TextField Value={this.state.Field1Value} />
<TextField Value={this.state.Field2Value} />
<TextField Value={this.state.Field2Value} />
{ /* ... */ }
<TextField Value={this.state.Field12Value} />
</>
)
}
}
class HeaderComponent extends Component {
token = null;
constructor(props) {
super(props);
this.state = {
MiscVariable: ""
};
}
setUpdateCurrentInvoice = (row) =>
{
this.props.setCurrentInvoice(row);
}
render() {
return (
<>
{this.props.AllInvoices.map((row, i) => (
<Button onClick={() => { this.setUpdateCurrentInvoice(row); }} />
))}
</>
)
}
}
Within the App component an AJAX call returns all invoices, and sets an initial value for this.state.CurrentInvoice. Afterwards, buttons in <HeaderComponent /> or <MainFormComponent /> can change CurrentInvoice.
When CurrentInvoice within App is changed, I want to trigger getInvoiceDetailsAndUpdateForm within the <MainFormComponent /> so that I can perform another AJAX call and run other business logic within that component.
What I'm finding is that within <MainFormComponent /> I can't seem to be able to "subscribe" to props.CurrentInvoice value changes. Within the render() method in that Component I see the change. But, that hasn't helped me because I want to trigger getInvoiceDetailsAndUpdateForm.
Does anyone have any ideas on how I can achieve the outcome I want?
Update:
The original code I published did not contain code related to the HeaderComponent. I've now included that to help draw the whole picture.
Basically what's happening is that the user clicks on a button in HeaderComponent which then calls setCurrentInvoice. When this happens componentDidUpdate is triggered, but only in the parent component. I am trying to figure out why componentDidUpdate within MainFormComponent is NOT also firing.
Solution 1:[1]
What you are looking for is componentDidUpdate. You can compare prevProps and currentProps and perform necessary actions
you would want to implement this lifecycle method in your MainFormComponent
Also, here's this example which is emulating the behavior you are having.
if you see App is the component storing the state and the handler,
and ChildComponent has the componentDidUpdate in it
now when you click either in App or AnotherChildComponent
it fires the state update, since ChildComponent has ComponentDidUpdate and it is receiving the counter prop you can clearly see it printing the previous and current values.
One thing to note - componentDidUpdate does not get immediately fired but when the state/prop changes so you may not see it fired on the first render but as soon as your api returns something and updates the state you will see it's getting fired for the 1st time in your MainForm component
Also, if you can post some proof of it not firing like screenshot of console.log would be helpful.
I don't see a point of componentDidUpdate not firing.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
