Подтвердить что ты не робот

ComponentWillReceiveProps и componentDidUpdate бесконечный цикл

Я пытаюсь обновить состояние после изменения состояния из дочернего компонента и без успеха, каждый раз, когда я вызываю функцию, я получаю переполнение стека, prop вызывает бесконечное время функции, но вопрос в том, что я действительно необходимо обновить это состояние и не знаю, как это решить в настоящее время.

Родитель

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,
};

Ребенок

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 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) - это корень моей проблемы, и мне действительно нужно это обновить, потому что я сравниваю массив внутри него с другим массивом

Обновление 1: Я знаю, что мое обращение к historyToggleStates выполняется в случаях с паролями, но, как вы видите, мне нужно обновить свое состояние от родителя, потому что я сравниваю это значение каждый раз в my componentWillReceprops из моего дочернего компонента.

Действительно ли нужен государственный менеджер для этой ситуации? Я следую советам Дэн Абрамов, и, не повышая сложность системы, любой совет будет оценен.

4b9b3361

Ответ 1

Детский компонент componentWillReceiveProps, который в первую очередь запускается, когда он обновляется родителем с новыми реквизитами, вызывает обновление родителя (с historyToggleStates).

Однако для этого цикла зависит от следующего:

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

то есть. если компонент входящий prop keyId совпадает с последним компонентом в истории keyId. Другими словами, когда дети обновляются, последний компонент в истории всегда будет запускать этот кодовый блок, если forceFlip является истинным.

forceFlip Значение зависит от следующего:

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

то есть. forceFlip устанавливается в true, если одновременно открываются любые последние две компоненты в истории.

Затем, как вы сказали, historyToggleStates запускается, родитель обновляется, дочерний componentWillReceiveProps запускается и цикл повторяется.

Возможное обходное решение

Теперь я понимаю, что было намеренно спроектировано, что если две последние карты были одновременно открыты, они должны быть принудительно перевернуты, т.е. закрыты.

Чтобы достичь этого, я бы сказал, что не сохраняйте состояние карты, локализованное в компоненте карты, но подтягивайте его до состояния компонентов родителей и не учитывайте компоненты дочерних карт. В родительском устройстве поддерживайте что-то вроде cardsState (возможно, лучшее имя) и используйте его, чтобы решить, должна ли открываться карта или нет. Вы можете закрыть последние две открытые карты, просто установив их состояние в false, если обнаружите, что они открыты в истории.

Это освободит вас от зависимости от переменной forceFlip и упростит ваш компонент для дочерних карт - откройте их, только если состояние открыто.

Ответ 2

ваш вызов historyToggleStates в обоих случаях с callForceFlip как истинный, что приводит к вызову forceFlipParent для родителя, который устанавливает childFlipToFalse до True (передано дочернему элементу как forceFlip)

Я считаю, что forceFlip всегда является истиной, является источником вашей проблемы.

Ответ 3

У меня была проблема, подобная этому, когда мой handleClick бесконечно зацикливался, попробуйте это для вашей функции onClick:

onClick={() => {this.checkOneOpened()}}

Ответ 4

  • Я думаю, проблема в этой части

    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**
    });}
    

    вы можете проверить значения условия if, чтобы после повторного отображения он не переходил в состояние.

  • prop-types перейдите в другой пакет, проверьте, чтобы вы не получили предупреждение для этого https://facebook.github.io/react/docs/typechecking-with-proptypes.html

  • Предложите изменить это свойство имени childFlipToFalse на ischildFlip

  • Здесь вы можете вынуть функцию обратного вызова, поскольку вы ее не используете.

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

    к

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

Это мое предложение: в этом случае вы можете сделать родительский компонент единственным компонентом класса Componente, а дочерние элементы - без него, это поможет улучшить структуру и управлять вашим дочерним компонентом, это заставит вас выполнять другую работу вокруг случай, с которым будет легче подойти