Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
125
rated 0 times [  127] [ 2]  / answers: 1 / hits: 29163  / 7 Years ago, wed, september 13, 2017, 12:00:00

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;



  • What am I missing?

  • Also, after I've received the 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??


More From » reactjs

 Answers
53

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
}

[#56497] Monday, September 11, 2017, 7 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
sonja

Total Points: 541
Total Questions: 113
Total Answers: 114

Location: Anguilla
Member since Sun, Jan 29, 2023
1 Year ago
sonja questions
Mon, Nov 30, 20, 00:00, 4 Years ago
Sun, Oct 11, 20, 00:00, 4 Years ago
Thu, May 21, 20, 00:00, 4 Years ago
Sun, Nov 10, 19, 00:00, 5 Years ago
Mon, Aug 26, 19, 00:00, 5 Years ago
;