创建一个无限循环

创建一个无限循环

本文介绍了为什么componentDidUpdate()创建一个无限循环?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Parent组件的state中存储了urltoken.我正在将urltoken作为props从父级Component传递给子级Component.但是,如果在父Component中发生某些事件,则会触发setState(),结果将执行子ComponentcomponentDidUpdate().
由于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 that url. For example <ListData /> component can handle only one type of url. How can I render another component within render() based on the url 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
      

      编辑
      我忘记提及的另一件事可能与您的问题没有直接关系,但应该强制执行,从不对此事在componentWillMountconstructor内部执行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()创建一个无限循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-12 07:40