I've stored url
and a token
in state
in Parent
component. I'm passing an url
and a token
as props
from parent Component
to child Component
. However, if there is some event in parent Component
, setState()
is triggered and as a result, componentDidUpdate()
of child Component
gets executed.
As componentDidUpdate()
was causing an infinite loop (as it triggers setState() inside child component), I've placed condition. But this does not prevent the error.
Child Component ie DisplayRevenue
is as follows:
import React, { Component } from 'react';
import '../App.css';
import ListData from './listdata.js'
var axios = require('axios');
class DisplayRevenue extends Component {
constructor(props){
super(props);
this.state = { data:[], url:"" }
console.log(this.props.url);
}
componentWillMount() {
this.loadRevenue(this.props.url, this.props.token);
}
componentDidUpdate(){ //creates infinite loop
// console.log(this.props.url);
this.loadRevenue(this.props.url, this.props.token);
}
setData(data){
//if(this.state.url != this.props.url){
if(this.state.data != data.data){
console.log(data.data); //(1)
// console.log(this.state.url); //(2)
this.setState(data:data);
console.log(this.state.data); //(3)
// console.log(this.props.url); //(4)
} //(1) & (3) yields exactly same value so does (2) & (4)
}
loadRevenue(url,token){
axios({
method:'get',
url:url,
headers: {
Authorization: `Bearer ${token}`,
},
})
.then( (response) => {
// console.log(response.data);
this.setData(response.data);
})
.catch(function (error) {
console.log("Error in loading Revenue "+error);
});
}
render() {
return (
<ListData data={this.state.data}/>
);
}
};
export default DisplayRevenue;
Parent Component ie MonthToDate is as below:
import React, { Component } from 'react';
import '../App.css';
import DisplayRevenue from './displayRevenue'
var axios = require('axios');
class MonthToDate extends Component {
constructor(props){
super(props);
this.state = {
data:null,
url:"http://localhost:3000/api/monthtodate"
}
//console.log(this.props.location.state.token);
}
groupBySelector(event){
if ((event.target.value)==="invoice"){
this.setState({url:"http://localhost:3000/api/monthtodate"})
} else if ((event.target.value)==="customer") {
this.setState({url:"http://localhost:3000/api/monthtodate?group-by=customerNumber"})
} else if ((event.target.value)==="month") {
this.setState({url:"http://localhost:3000/api/invoices?group-by=month"})
} else {
this.setState({url:"http://localhost:3000/api/monthtodate"})
}
console.log(this.state.url);
}
render() {
return (
<div>
<select onChange={(event)=>this.groupBySelector(event)}>
<option value="invoice">GROUP BY INVOICE</option>
<option value="customer">GROUP BY CUSTOMER</option>
<option value="month">GROUP BY MONTH</option>
</select>
<DisplayRevenue url={this.state.url} token={this.props.location.state.token}/>
</div>
);
}
}
export default MonthToDate;
url
in the child component I want to render different component based on that url
. For example <ListData />
component can handle only one type of url
. How can I render another component within render()
based on the url
type??Tip: To avoid an infinite loop, all the network requests are needed to be inside a conditional statement as: componentDidUpdate(prevProps, prevState) { if (prevState. data !== this.
The componentDidUpdate function is a part of a React component's life cycle. It is called when a component got updated. This might happen if new props have been provided by a parent component or an internal state has been changed.
The componentDidUpdate event is triggered whenever there is a state or props update. ComponentDidUpdate() has access to three properties, two of which are most often used more than the third. These are prevProps, prevState and lastly, snapshot, which is used less often.
componentDidMount() : invoked immediately after a component is mounted (inserted into the DOM tree) componentDidUpdate(prevProps, prevState, snapshot) : is invoked immediately after updating occurs. This method is not called for the initial render.
You are calling an ajax call in componentDidUpdate
, and you set the state on the callback, that will trigger another call and update which will call the ajax request again and callback will set state again and so on.
Your condition in setData
:
if(this.state.data != data.data)
will always return true as objects are reference type and can't be compared, no matter what data returned from the ajax call it will always be a different object and will return true
in your condition.
Example:
var obj1 = {a:1}
var obj2 = {a:1}
console.log(obj1 != obj2); // returns true
What you can do, is compare primitives values inside the two objects.
For example:
if(this.state.data.id != data.id) // id could be a string or a number for example
EDIT
Another thing i forgot to mention which may not relate to your problem directly but should be enforced, Never do ajax requests inside componentWillMount
or the constructor
for that matter, as the render function will be invoked before your ajax request will finish. you can read about it in the DOCS.
Ajax requests should be invoked in componentDidMount
life cycle method instead.
EDIT #2
Another thing that can be helpful, in the MonthToDate
render function you are passing a new instance of a function on each render (which may cause a performance hit)
<select onChange={(event)=>this.groupBySelector(event)}>
Try changing it to this (the event will be passed automatically to the handler):
<select onChange={this.groupBySelector}>
You would also need to bind it in the constructor:
constructor(props){
super(props);
this.state = {
data:null,
url:"http://localhost:3000/api/monthtodate"
}
//console.log(this.props.location.state.token);
this.groupBySelector = this.groupBySelector.bind(this); // binds this to the class
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With