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

标签 javascript reactjs ecmascript-6

我已经存储了 url和一个 tokenstateParent零件。我正在传递 url和一个 token作为props来自 parent Component给 child Component .但是,如果父级中有一些事件 Component , setState()被触发,结果,componentDidUpdate() child 的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;
  • 我错过了什么?
  • 此外,在我收到 url 之后在子组件中,我想根据 url 渲染不同的组件.例如<ListData />组件只能处理一种类型的 url .如何在 render() 中渲染另一个组件基于 url输入??

最佳答案

你在 componentDidUpdate 中调用了一个 ajax 调用,你在回调上设置了状态,这将触发另一个调用和更新,这将再次调用 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

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

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

编辑
我忘记提及的另一件事可能与您的问题没有直接关系,但应该强制执行,从不componentWillMountconstructor 中执行 ajax 请求这很重要,因为渲染函数将在您的 ajax 请求完成之前被调用。您可以在 DOCS 中阅读相关信息.
Ajax 请求应该在 componentDidMount 中调用 life cycle method相反。

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

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

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

 <select onChange={this.groupBySelector}>  

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

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
  }

关于javascript - 为什么 componentDidUpdate() 会创建一个无限循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46195559/

相关文章:

javascript - 如何在 Laravel 中以不同方式比较日期和时间

javascript - Mongoose - FindOne 和 Save 不起作用

javascript - 如何将对象转换为对象数组?

javascript - 使用 mousedown 模拟 Spinner 数字

javascript - 如何在 ajax 事件(onreadystatechange)上创建函数

javascript - ReactJS使用三元运算符渲染span标签

javascript - 如何通过快捷方式添加特殊字符到任何输入?

javascript - 访问返回的 promise 的结果

node.js - 选择器存在,但无法获取有关 Node 的信息,因为指定的选择器与 DOM 树中的任何 Node 都不匹配

javascript - 如何测试包含导入的异步方法的类?