J'ai stocké url
et un token
dans state
dans Parent
composant. Je passe un url
et un token
comme props
du parent Component
à l'enfant Component
. Cependant, s'il existe un événement dans le parent Component
, setState()
est déclenché et, par conséquent, componentDidUpdate()
de l'enfant Component
est exécuté.
Comme componentDidUpdate()
provoquait une boucle infinie (car elle déclenche setState () à l'intérieur du composant enfant), j'ai placé la condition. Mais cela n'empêche pas l'erreur.
Composant enfant, c'est-à-dire DisplayRevenue
, est le suivant:
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;
Le composant parent, c'est-à-dire MonthToDate, est le suivant:
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
dans le composant enfant, je veux rendre un autre composant basé sur ce url
. Par exemple, le composant <ListData />
Ne peut gérer qu'un seul type de url
. Comment rendre un autre composant dans render()
basé sur le type url
??Vous appelez un appel ajax dans componentDidUpdate
et vous définissez l'état sur le rappel, ce qui déclenchera un autre appel et une mise à jour qui appellera à nouveau la demande ajax et le rappel définira à nouveau l'état, etc.
Votre état dans setData
:
if(this.state.data != data.data)
retournera toujours vrai car les objets sont de type référence et ne peuvent pas être comparés, quelles que soient les données renvoyées par l'appel ajax, ce sera toujours un objet différent et renverra true
dans votre condition. Exemple:
var obj1 = {a:1}
var obj2 = {a:1}
console.log(obj1 != obj2); // returns true
Ce que vous pouvez faire, c'est comparer les valeurs primitives à l'intérieur des deux objets.
Par exemple:
if(this.state.data.id != data.id) // id could be a string or a number for example
[~ # ~] modifier [~ # ~]
Une autre chose que j'ai oublié de mentionner qui peut ne pas être directement liée à votre problème mais qui devrait être appliquée, Jamais faire des requêtes ajax dans componentWillMount
ou constructor
d'ailleurs, car la fonction de rendu sera invoquée avant la fin de votre requête ajax. vous pouvez le lire dans les DOCUMENTS .
Les requêtes Ajax doivent être invoquées dans componentDidMount
méthode du cycle de vie à la place.
EDIT # 2
Une autre chose qui peut être utile, dans la fonction de rendu MonthToDate
, vous passez une nouvelle instance d'une fonction sur chaque rendu (ce qui peut entraîner un impact sur les performances)
<select onChange={(event)=>this.groupBySelector(event)}>
Essayez de le changer en ceci (l'événement sera transmis automatiquement au gestionnaire):
<select onChange={this.groupBySelector}>
Vous devrez également le lier dans le constructeur:
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
}