小编典典

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

reactjs

我已经存储url了一个tokenin statein
Parent组件。我传递的urltokenprops从父Component到子Component。然而,如果在父母一些事件ComponentsetState()被触发,因此,componentDidUpdate()儿童Component被执行。
由于componentDidUpdate()造成了无限循环 (因为它触发了子组件内部的setState()) ,所以我放置了条件。但这不能防止错误。
子组件即DisplayRevenue如下:

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如下:

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;
  • 我想念什么?
  • 另外,在收到urlin子组件之后,我想基于那个渲染另一个组件url。例如<ListData />component只能处理一种类型的url。如何render()根据url类型在其中渲染另一个组件?

阅读 595

收藏
2020-07-22

共1个答案

小编典典

您正在中调用ajax调用componentDidUpdate,并在回调上设置了状态,这将触发另一个调用并进行更新,这将再次调用ajax请求,而回调将再次设置状态,依此类推。
您的条件setData

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

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

var obj1 = {a:1}

var obj2 = {a:1}



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

您可以做的就是比较两个对象中的原始值。
例如:

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

编辑
我忘记提及的另一件事可能与您的问题没有直接关系,但应该强制执行, 切勿
在内部componentWillMount或之内进行Ajax请求constructor,因为在您的Ajax请求完成之前将调用render函数。您可以在DOCS中阅读有关它的信息。
应该在componentDidMount 生命周期方法中调用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
  }
2020-07-22