Когда использовать метод жизненного цикла componentWillReceiveProps?

Я новичок в React/Redux и имею проблему с состоянием.

TrajectContainer.jsx

class TrajectContainer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            trajects: props.trajects,
            onClick: props.onClick
        };
    }

    componentWillReceiveProps(nextProps) {
        console.log('componentWillReceiveProps', nextProps);
        this.setState(nextProps);
    }

    render() {
        // When the componentWillReceiveProps is not present, the this.state will hold the old state
        console.log('rerender', this.state);
        return (<div className="col-md-6">
            <h2>Trajects</h2>
            <button className="btn btn-primary" onClick={this.state.onClick}>Add new Traject</button>
            {this.state.trajects.map(traject => <Traject traject={traject} key={traject.name}/>)}
        </div>)
    }
}

const mapStateToProps = function (store) {
    console.log('mapToStateProps', store);
    return {
        trajects: store.trajects
    };
};

const mapDispatchToProps = function (dispatch, ownProps) {
    return {
        onClick: function () {
            dispatch(addTraject());
        }
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(TrajectContainer);

Когда редуктор возвращает новое состояние, компонент будет переписываться с новыми данными.

Однако: если я удаляю функцию componentWillReceiveProps, функция render() имеет старое состояние.

Я проверил данные, полученные в mapStateToProps, и это новое новое состояние. Поэтому я не понимаю, зачем мне нужна функция componentWillReceiveProps, чтобы функция рендеринга получала новые данные.

Я делаю что-то неправильно?

Ответ 1

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


В вашем случае, почему вам нужен этот метод componentWillReceiveProps?

Поскольку вы сохраняете значения реквизита в переменной состояния и используете его следующим образом:

this.state.KeyName

Для этого вам понадобится метод жизненного цикла componentWillReceiveProps для обновления значения состояния с новым значением реквизита, будут обновляться только значения реквизита компонента, но автоматически состояние не будет обновляться. Если вы не обновите состояние, то this.state всегда будет иметь исходные данные.

componentWillReceiveProps не требуется, если вы не храните значения реквизита в состоянии и напрямую используете:

this.props.keyName

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

Согласно DOC:

componentWillReceiveProps() вызывается до того, как смонтированный компонент получит новые реквизиты. Если вам нужно обновить состояние в ответ на изменения prop (например, для его сброса), вы можете сравнить this.props и nextProps и выполнить переходы состояний, используя this.setState() в этом методе.

Реагент не вызывает компонент componentWillReceiveProps с начальными реквизитами во время монтажа. Он вызывает этот метод только в том случае, если некоторые реквизиты компонентов могут обновляться.

Предложение:

Не сохраняйте значения реквизита в состоянии, напрямую используйте this.props и создайте компоненты ui.

Ответ 2

У меня была аналогичная проблема с withRouter():

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(TrajectContainer));

Ответ 3

Обновить

componentDidUpdate()

теперь должен использоваться, а не componentWillReceiveProps

также см. статью от gaearon о переписывании упругих компонентов

Здесь есть две потенциальные проблемы

  1. Не переназначайте ваши реквизиты, чтобы утверждать, что вы используете избыточность для извлечения значений из хранилища и возврата их в качестве реквизитов в ваш компонент

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

  1. Вам не нужно переносить свои действия в рассылку, если вы передаете mapDispatcahToProps. Если объект передается, предполагается, что каждая функция внутри него является создателем действия. Будет возвращен объект с теми же именами функций, но с каждым создателем действия, заключенным в диспетчер

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

import React from "react";
import { connect } from "react-redux";

const TrajectContainer = ({ trajects, addTraject }) => (
	<div className="col-md-6">
		<h2>Trajects</h2>
		<button className="btn btn-primary" onClick={addTraject}>Add new Traject</button>
		{trajects.map(traject => <Traject traject={traject} key={traject.name} />)}
	</div>
);

const mapStateToProps = ({ trajects }) => ({ trajects });

export default connect( mapStateToProps, { addTraject })(TrajectContainer);

Ответ 4

Проблема с вашей реализацией заключается в том, что вы дублируете состояние хранилища Redux (исходя из реквизита) в ваше состояние React (this.state)

В вашем примере, если store.trajects обновлен, то this.props.traject будет обновлен, и рендер будет запущен, только если this.props.traject используется в вашем методе рендеринга (не в случае).

Поскольку вы используете состояние вместо prop в методе рендеринга, вам нужно вручную изменить состояние вашего компонента, используя this.setState чтобы вызвать рендер.

Это не очень хорошая модель: я бы посоветовал не использовать это состояние и напрямую использовать реквизиты:

class TrajectContainer extends React.Component {
    render() {
        return (<div className="col-md-6">
            <h2>Trajects</h2>
            <button className="btn btn-primary" onClick={this.props.onClick}>Add new Traject</button>
        {this.props.trajects.map(traject => <Traject traject={traject} key={traject.name}/>)}
    </div>)
}
}
const mapStateToProps = function (store) {
  console.log('mapToStateProps', store);
  return {
    trajects: store.trajects
  };
};

const mapDispatchToProps = function (dispatch, ownProps) {
   return {
      onClick: function () {
        dispatch(addTraject());
      }
  }
};

export default connect(mapStateToProps, mapDispatchToProps)(TrajectContainer)

Ответ 5

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

  • В вашем конструкторе вы заявили свое состояние, как показано ниже. Как вы можете видеть, вы строите свое state используя реквизиты, которые передаются. (Вот почему вам требуется componentWillReceiveProps и логика для его обновления)

    this.state = {
        trajects: props.trajects,
        onClick: props.onClick
    };
    
  • Поэтому, когда ваши реквизиты, изменения componentWillReceiveProps - это функция, которая вызывается. Конструктор не вызван. Поэтому вам нужно снова установить состояние, чтобы изменения переходили в состояние компонента.

  • Однако ваша логика должна быть такой, как показано ниже. С условием, чтобы вы могли предотвратить повторные обновления состояния, если его вызывали несколько раз.

    componentWillReceiveProps(nextProps) {
     console.log('componentWillReceiveProps', nextProps);
     if (this.props !== nextProps) {
      this.setState(nextProps);
     }
    }
    

Следует хранить реквизит в состоянии, только если вы собираетесь изменить его содержимое. Но в вашем случае я вижу, что изменений нет. Таким образом, вы можете напрямую использовать this.props.trajects напрямую, а не хранить его в состоянии, а затем использовать его. Таким образом, вы можете избавиться от componentWillReceiveProps Таким образом, ваша функция рендеринга будет использовать что-то вроде ниже

{this.props.trajects.map(traject => //what ever is your code.... }