为什么componentDidUpdate()会创建一个无限循环?

时间:2017-09-13 10:49:55

标签: javascript reactjs ecmascript-6

我已将urltoken存储在state组件的Parent中。我将urltoken作为props从父Component传递给子Component。但是,如果父Component中存在某个事件,则会触发setState(),结果导致componentDidUpdate()的{​​{1}}个孩子被执行。
由于Component导致无限循环(因为它触发子组件内的setState()),我已经放置了条件。但这并不能防止错误 子组件即componentDidUpdate()如下:

DisplayRevenue

父组件即MonthToDate如下:

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;
  • 我缺少什么?
  • 此外,在我收到子组件中的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类型??
  • render()内呈现另一个组件

1 个答案:

答案 0 :(得分:6)

你正在调用componentDidUpdate中的ajax调用,并在回调上设置状态,这将触发另一个调用和更新,它将再次调用ajax请求,并且callback将再次设置状态,依此类推。登记/> 您在setData的条件:

if(this.state.data != data.data) 

将始终返回true,因为对象是引用类型且无法进行比较,无论从ajax调用返回什么数据,它始终是不同的对象,并且将在您的条件中返回true。 示例:

var obj1 = {a:1}
var obj2 = {a:1}

console.log(obj1 != obj2); // returns true

您可以做的是比较两个对象内的primitives值 例如:

if(this.state.data.id != data.id) // id could be a string or a number for example

修改
我忘记提及的另一件事可能与您的问题没有直接关系但应该强制执行,从不componentWillMountconstructor内执行ajax请求,因为渲染在您的ajax请求完成之前将调用该函数。你可以在DOCS中阅读它 应该在componentDidMount life cycle method中调用Ajax请求。

编辑#2
另一件事可能有用,在MonthToDate渲染函数中,您在每个渲染上传递一个新的函数实例(这可能会导致性能下降)

<select onChange={(event)=>this.groupBySelector(event)}>

尝试将其更改为此(事件将自动传递给处理程序):

 <select onChange={this.groupBySelector}>  

您还需要在构造函数中绑定它:

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
  }