【问题标题】:When to use componentWillReceiveProps lifecycle method?何时使用 componentWillReceiveProps 生命周期方法?
【发布时间】:2018-02-17 04:15:32
【问题描述】:

我是 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);

当 reducer 返回新状态时,组件将使用新数据重新渲染。

但是:如果我删除了 componentWillReceiveProps 函数,render() 函数将具有旧状态。

我检查了在 mapStateToProps 中收到的数据,这是新的 New State。 所以我不明白为什么我需要 componentWillReceiveProps 函数才能让渲染函数接收新数据。

我做错了吗?

【问题讨论】:

  • 您不需要state 来处理这些更改。 trajects 属于stateprops(来自redux 的connect)。此外,在每个 componentWillReceiveProps 上设置状态不是一个好习惯。我会从状态中删除 trajects 并仅使用传入的道具。它们会在必要时更改,您将始终拥有最新的值。
  • @HemersonCarlin 这就是我想要的,但是 render() 中的状态不是从 mapStateToProps 传递的最新状态,这是我的问题。我只有在使用 .setState 时才获得新状态,这就是我制作这个 StackOverflow Q 的原因!
  • 尝试删除状态并改用this.props.trajects

标签: reactjs react-redux


【解决方案1】:

更新

componentDidUpdate()

现在应该使用而不是 componentWillReceiveProps

另见 article from gaearon 重写弹性组件

这里有两个潜在的问题

  1. 不要重新分配你的 props 来声明你正在使用 redux 从 store 中提取值并将它们作为 props 返回到你的组件

避免状态意味着您不再需要构造函数或生命周期方法。因此,您的组件可以编写为无状态功能组件,以这种方式编写您的组件有性能优势。

  1. 如果您正在传递 mapDispatcahToProps,则无需将操作包装在调度中。如果传递了一个对象,则假定其中的每个函数都是一个动作创建者。将返回一个具有相同函数名称但每个动作创建者都封装在调度中的对象

下面是一段代码 sn-p,它从你的组件中移除状态并依赖于从 redux 存储返回的状态

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

【讨论】:

    【解决方案2】:

    如果你想用新的 props 值更新 state 值,则需要componentWillReceiveProps,只要 props 值发生任何变化,就会调用此方法。


    在你的情况下,为什么你需要这个 componentWillReceiveProps 方法?

    因为您将道具值存储在状态变量中,并像这样使用它:

    this.state.KeyName

    这就是为什么你需要 componentWillReceiveProps 生命周期方法来用新的 props 值更新 state 值,只有组件的 props 值会被更新,但状态不会自动更新。如果您不更新状态,那么this.state 将始终拥有初始数据。

    componentWillReceiveProps如果不将props值保存在state中直接使用就不需要了:

    this.props.keyName

    现在 react 将始终在 render 方法中使用更新后的 props 值,如果 props 发生任何变化,它将使用新的 props 重新渲染组件。

    根据DOC

    componentWillReceiveProps() 在挂载组件之前调用 收到新道具。如果您需要更新状态以响应 prop 更改(例如,重置它),您可以比较 this.props 和 nextProps 并使用 this.setState() 执行状态转换 这种方法。

    React 不会在使用初始 props 调用 componentWillReceiveProps 安装。如果组件的某些道具可能会调用此方法 更新。

    建议:

    state中不要存储props值,直接使用this.props创建ui组件。

    【讨论】:

    • 这是一个很好的答案!然而,任何看到这一点的人都应该知道,到目前为止,“componentWillReceiveProps()”方法已被弃用。您应该使用“componentDidUpdate(prevProps, prevState, snapshot)”。请参阅此处以获取更多说明:reactjs.org/docs/react-component.html#componentdidupdate
    【解决方案3】:

    您的实现问题是您将 Redux 存储状态(来自道具)复制到您的 React 状态(this.state)

    在您的示例中,如果 store.trajects 被更新,那么 this.props.traject 将被更新,并且只有在您的渲染方法中使用 this.props.traject 时才会触发渲染(不是这种情况)。

    由于您在渲染方法中使用 state 而不是 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)
    

    【讨论】:

      【解决方案4】:

      在您的情况下,您将需要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.... }
      

      【讨论】:

      • 谢谢黑豹,你的解释很清楚!我确实计划扩展组件并允许修改某些道具,因此我将使用您的示例!
      【解决方案5】:

      我有类似的问题添加 withRouter() 像这样:

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

      【讨论】:

        猜你喜欢
        • 2018-09-12
        • 1970-01-01
        • 1970-01-01
        • 2016-12-20
        • 2018-08-05
        • 1970-01-01
        • 1970-01-01
        • 2017-02-28
        • 1970-01-01
        相关资源
        最近更新 更多