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

IT技术 javascript reactjs ecmascript-6
2021-03-29 03:56:05

我已经存储urltokenstateParent的组成部分。我正在将 anurl和 a tokenasprops从 parentComponent传递给 child Component但是,如果 parent 中存在某些事件ComponentsetState()则会触发并因此执行componentDidUpdate()child Component
由于componentDidUpdate()导致无限循环(因为它在子组件内触发 setState() ),我已经放置了条件。但这并不能防止错误。
子组件ieDisplayRevenue如下:

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;
  • 我错过了什么?
  • 此外,在我收到url子组件中的 之后,我想基于该 呈现不同的组件url例如<ListData />组件只能处理一种类型的url. 如何render()根据url类型渲染另一个组件
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

编辑
我忘了提到的另一件事可能与您的问题没有直接关系,但应该强制执行,永远不要在内部执行 ajax 请求componentWillMountconstructor就此而言,因为渲染函数将在您的 ajax 请求完成之前被调用。你可以在DOCS 中阅读它
Ajax 请求应该在componentDidMount 生命周期方法中调用

编辑 #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
  }
应该在componentDidMount(答案更新)中调用@JDHrnnts ajax 请求文件
2021-05-26 03:56:05
@Sag1vcomponentWillMount您建议调用 ajax 的替代方法是什么我大部分时间都在这样做,但我不确定这是正确的方法。也许你也可以将它包含在你的答案中
2021-05-30 03:56:05
行。那请告诉我该怎么办?
2021-06-03 03:56:05
我也试过比较两个字符串if(this.state.url != this.props.url)(在代码中注释)它也不起作用。尽管在无限循环中,当我记录他们的 o/p 时,它们完全相同。
2021-06-18 03:56:05