问题描述
我在Parent
组件的state
中存储了url
和token
.我正在将url
和token
作为props
从父级Component
传递给子级Component
.但是,如果在父Component
中发生某些事件,则会触发setState()
,结果将执行子Component
的componentDidUpdate()
.
由于componentDidUpdate()
导致了无限循环(因为它触发了子组件内的setState()),所以我放置了条件.但这不能防止错误.
子组件,即DisplayRevenue
如下:
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;
父组件,即MonthToDate如下:
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
后,我想基于该url
渲染不同的组件.例如,<ListData />
组件只能处理url
的一种类型.如何根据url
类型在render()
中渲染另一个组件? - What am I missing?
- Also, after I've received the
url
in the child component I want to render different component based on thaturl
. For example<ListData />
component can handle only one type ofurl
. How can I render another component withinrender()
based on theurl
type??
推荐答案
您正在componentDidUpdate
中调用ajax调用,并在回调中设置状态,这将触发另一个调用并更新,该调用将调用ajax请求再次,回调将再次设置状态,依此类推.
您在setData
中的情况:
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)
将始终返回true,因为对象是引用类型,并且无法进行比较,无论从ajax调用返回的数据是什么,它始终是不同的对象,并会在您的情况下返回true
.示例:
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
编辑
我忘记提及的另一件事可能与您的问题没有直接关系,但应该强制执行,从不对此事在componentWillMount
或constructor
内部执行ajax请求,因为将调用render函数.在您的Ajax请求完成之前.您可以在 DOCS 中进行阅读.
应该在componentDidMount
生命周期方法中调用Ajax请求
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.
编辑#2
另一件事可能会有所帮助,在MonthToDate
渲染函数中,您正在每个渲染器上传递该函数的新实例(这可能会导致性能下降)
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
}
这篇关于为什么componentDidUpdate()创建一个无限循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!