简体   繁体   English

componentWillReceiveProps和componentDidUpdate无限循环

[英]componentWillReceiveProps and componentDidUpdate infinite Loop

I'm trying to update the state after changing state from a child component and without success, every time I call the function I got a stack overflow, the prop is calling infinite times the function, but the question is, I really need to update this state and have no idea how to solve this currently. 我试图在从子组件改变状态后更新状态并且没有成功,每次我调用函数时我得到堆栈溢出,prop都调用无限次函数,但问题是,我真的需要更新这种状态,目前还不知道如何解决这个问题。

Parent

import React, { PropTypes, Component } from 'react';
import Card from './card/card.js';
import style from './style.scss';

class Container extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isFlipped: false,
      oneOpened: true,
      history: [],
      childFlipToFalse: false,
    };
    this.historyToggleStates = this.historyToggleStates.bind(this);
    this.forceFlipParent = this.forceFlipParent.bind(this);
    this.checkForceFlip = false;
  }
  historyToggleStates(bool, id, callForceFlip) {
    this.setState({
      history: this.state.history.concat([{ opened: bool, id }]),
    }, () => {
      console.log('inside historyToggleStates');
      if (callForceFlip) {
        this.forceFlipParent()
      }
    });
  }
  forceFlipParent() {
    const { history } = this.state;
    const first = history[0];
    const last = history[history.length - 1];
    const beforeLast = history[history.length - 2];
    console.log('force FLIP PARENT');
    if (history.length > 1) {
      if (JSON.stringify(last.opened) === JSON.stringify(beforeLast.opened)) {
        this.setState({ childFlipToFalse: true });
      }
    }
  }
  render() {
    const rest = {
      basePath: this.props.basePath,
      backCard: this.props.backCard,
      isShowing: this.props.isShowing,
      historyToggleStates: this.historyToggleStates,
      isOpened: this.state.isOpened,
      isFlipped: this.state.isFlipped,
      checkOneOpened: this.checkOneOpened,
      history: this.state.history,
      forceFlip: this.state.childFlipToFalse,
      flipToFalse: this.forceFlipParent,

    };
    const cardsMap = this.props.cards.map((item, key) => {
      return (
        <Card
          item={item}
          keyId={key}
          {...rest}
        />
      );
    });
    return (
      <div className="col-lg-12 text-center">
        {cardsMap}
      </div>
    );
  }
}


export default Container;

Container.propTypes = {
  cards: PropTypes.array.isRequired,
  item: PropTypes.func,
  basePath: PropTypes.string,
  backCard: PropTypes.string,
  isShowing: PropTypes.bool,
};

Child 儿童

import React, { Component, PropTypes } from 'react';
import ReactCardFlip from 'react-card-flip';
import style from './style.scss';

class Card extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isFlipped: false,
      update: false,
      id: 9999999,
    };
    this.handleClick = this.handleClick.bind(this);
    this.checkOneOpened = this.checkOneOpened.bind(this);
  }
  componentWillReceiveProps(nextprops) {
    const { history, isFlipped, historyToggleStates } = this.props;
    const last = nextprops.history[nextprops.history.length - 1];
    const beforeLast = nextprops.history[nextprops.history.length - 2];
    console.log(history);
    console.log(nextprops.history);
    if (nextprops.forceFlip && last.id === nextprops.keyId) {
      this.setState({ isFlipped: !this.state.isFlipped, update: true, id: last.id }, () => {
        console.log('callback willreceiveprops', this.state.isFlipped);
        historyToggleStates(this.state.isFlipped, nextprops.keyId, true, this.state.update);  **<--- Here's my problem**
      });
    }
    if (nextprops.forceFlip && beforeLast.id === nextprops.keyId) {
      this.setState({ isFlipped: !this.state.isFlipped, update: true, id: beforeLast.id }, () => {
      });
    }
  }

  handleClick(e, nextState, id) {
    const { keyId, historyToggleStates, forceFlip } = this.props;
    if (e) {
      e.preventDefault();
    }
    if (!nextState) {
      this.setState({ isFlipped: !this.state.isFlipped }, () => {
        historyToggleStates(this.state.isFlipped, keyId, true, this.state.update);
      });
    } else {
      // historyToggleStates(nextState, id, false);
      return 0;
    }
  }

  checkOneOpened(e) {
    if (!this.props.isShowing) {
      this.handleClick(e);
    }
  }

  render() {
    const { item, basePath, backCard, isShowing, isFlipped, forceFlip } = this.props;
    return (
      <div className={`col-lg-2 col-md-3 col-sm-6 ${style.card}`}>
        <ReactCardFlip
          isFlipped={this.state.isFlipped}
          flipSpeedBackToFront={0.9}
          flipSpeedFrontToBack={0.9}
        >
          <div key="front">
            <button
              onClick={() => {this.checkOneOpened()}}
            >
              <img src={isShowing ? `${basePath}${item.image}` : backCard} alt={item.name} className={`${style.img}`} />
            </button>
          </div>
          <div key="back">
            <button
              onClick={() => {this.checkOneOpened()}}
            >
              <img src={isShowing ? backCard : `${basePath}${item.image}`} alt={item.name} className={`${style.img}`} />
            </button>
          </div>
        </ReactCardFlip>
      </div>
    );
  }
}

export default Card;


Card.propTypes = {
  basePath: PropTypes.string,
  backCard: PropTypes.string,
  isShowing: PropTypes.bool,
  historyToggleStates: PropTypes.func,
  isOpened: PropTypes.bool,
  isFlipped: PropTypes.bool,
  checkOneOpened: PropTypes.func,
};

historyToggleStates(this.state.isFlipped, nextprops.keyId, true, this.state.update) is the root of my issue, and I really need to update this because I'm comparing the array inside him with another array historyToggleStates(this.state.isFlipped,nextprops.keyId,true,this.state.update)是我的问题的根源,我真的需要更新它,因为我正在比较他里面的数组和另一个数组

Update 1: I know that my calling to historyToggleStates is being done in the couple cases, but as you can see I need to update my state from the parent because I compare this value every time in my componentWillReceiprops from my child component. 更新1:我知道我对historyToggleStates的调用是在几个案例中完成的,但是你可以看到我需要从父级更新我的状态,因为我每次都在我的componentWillReceiprops中从我的子组件中比较这个值。

Is it really necessary a state manager for this situation? 对于这种情况,是否真的有必要成为州经理? I'm following the tips from Dan Abramov , and avoiding raise the complexity of the system, any tip would be appreciated. 我正在遵循Dan Abramov的提示,并且避免提高系统的复杂性,任何提示都会受到赞赏。

your calling historyToggleStates in both case with callForceFlip as true, which causes forceFlipParent on the parent to be invoked which sets childFlipToFalse to True (passed to the child as forceFlip ) 在这两种情况下,与callForceFlip为真,这将导致forceFlipParent上被调用父这台childFlipToFalseTrue您的通话historyToggleStates(传递给孩子的forceFlip)

i believe forceFlip being always true is the source of your problem. 我相信forceFlip永远是真的是你问题的根源。

The child component's componentWillReceiveProps , which in the first place is triggered when it is updated by the parent with new props, trigger's an update in the parent (with historyToggleStates ). 子组件的componentWillReceiveProps ,它首先被父级用新的props更新时触发,触发父级的更新(使用historyToggleStates )。

However for this cycle depends on the following: 但是,此循环取决于以下内容:

if (nextprops.forceFlip && last.id === nextprops.keyId) {

ie if component's incoming prop's keyId is same as last component in the history's keyId . 即,如果组件的传入prop的keyId与历史记录的keyId中的最后一个组件相同。 In other words, when the children get updated, the last component in the history will always run this code block provided forceFlip is true. 换句话说,当子项更新时,如果forceFlip为true,则历史记录中的最后一个组件将始终运行此代码块。

forceFlip 's value depends on the following: forceFlip的值取决于以下内容:

    if (history.length > 1) {
       if (JSON.stringify(last.opened) === JSON.stringify(beforeLast.opened)) {

ie forceFlip is set to true if any last two components in the history are simultaneously open. 即,如果历史记录中的最后两个组件同时打开,则forceFlip设置为true。

Then, as you said historyToggleStates is triggered, parent gets updated, child's componentWillReceiveProps gets triggered and the cycle repeats. 然后,正如您所说,触发了historyToggleStates ,父级得到更新,子级的componentWillReceiveProps被触发并且循环重复。

Possible Workaround 可能的解决方法

Now I take it that it was intentionally designed that if last two cards were simultaneously open, they must be force flipped ie closed. 现在我认为它是故意设计的,如果最后两张牌同时打开,它们必须被强制翻转,即关闭。

To achieve this, I'd say don't keep the state of the card localised in the card component, but pull it up to the parents components state and keep the children card components agnostic. 为了达到这个目的,我要说不要将卡的状态保持在卡组件中,而是将其拉到父组件状态并保持子卡组件不可知。 In the parent, maintain something like cardsState (maybe a better name) and use it to decide whether a card must be open or not. 在父级中,维护类似cardsState (可能是更好的名称)的东西,并使用它来决定是否必须打开卡。 You can close the last two simultaneously open cards by simply setting their state to false if you detect that they are open in the history. 如果您检测到它们在历史记录中处于打开状态,则可以通过简单地将其状态设置为false来关闭最后两张同时打开的卡片。

This will free you from the dependency on forceFlip variable and will keep you children card component simpler - open them only if the state says open. 这将使您免于依赖forceFlip变量,并使您的子卡组件更简单 - 只有在状态为打开时才打开它们。

我有一个与此类似的问题,我的handleClick无限循环,请尝试使用onClick函数:

onClick={() => {this.checkOneOpened()}}
  • I thinks the problem is in this part 我认为问题出在这一部分

     if (nextprops.forceFlip && last.id === nextprops.keyId) { this.setState({ isFlipped: !this.state.isFlipped, update: true, id: last.id }, () => { console.log('callback willreceiveprops', this.state.isFlipped); historyToggleStates(this.state.isFlipped, nextprops.keyId, true, this.state.update); // **<--- Heres my problem** });} 

    you can check the values of the if condition so after the re-rendered it not going inside condition. 你可以检查if条件的值,这样在重新渲染后它不会进入内部条件。

  • The prop-types have move to a different package, check out so you don't get a warning for that https://facebook.github.io/react/docs/typechecking-with-proptypes.html prop-types已转移到另一个包,检查出来,因此您不会收到警告https://facebook.github.io/react/docs/typechecking-with-proptypes.html

  • Suggest to change that name property of childFlipToFalse to ischildFlip 建议将childFlipToFalse name属性childFlipToFalseischildFlip

  • Here you can take out the call back function as you are not use it 在这里你可以取出回调功能,因为你没有使用它

     this.setState({ isFlipped: !this.state.isFlipped, update: true, id: beforeLast.id }, () => {}); 

    to

     this.setState({ isFlipped: !this.state.isFlipped, update: true, id: beforeLast.id }); 

This is my suggestion: In this case you can make the parent component the only class Componente and the children a staless component this will help have a better structure and manage your child component, this will make you do a different work around of the case that will be more easy to approach 这是我的建议:在这种情况下,你可以使父组件成为唯一的类Componente,子组件是一个staless组件,这将有助于拥有更好的结构和管理你的子组件,这将使你做一个不同的工作,会更容易接近

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM