何时使用 componentWillReceiveProps 生命周期方法?

IT技术 reactjs react-redux
2021-04-11 11:49:44

我是 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 函数才能让渲染函数接收新数据。

难道我做错了什么?

5个回答

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


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

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

this.state.KeyName

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

componentWillReceiveProps 如果您不将 props 值存储在 state 中并直接使用,则不需要:

this.props.keyName

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

根据DOC

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

React 在挂载期间不会使用初始props调用 componentWillReceiveProps。仅当某些组件的 props 可能更新时才调用此方法。

建议:

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

这是一个很好的答案!然而,任何看到这一点的人都应该知道,截至目前,“componentWillReceiveProps()”方法已被弃用。您应该使用“componentDidUpdate(prevProps, prevState, snapshot)”。请参阅此处以获取进一步说明:reactjs.org/docs/react-component.html#componentdidupdate
2021-06-17 11:49:44

更新

componentDidUpdate()

现在应该使用而不是 componentWillReceiveProps

另请参阅gaearon一篇文章重新编写弹性组件

这里有两个潜在的问题

  1. 不要将您的props重新分配给您正在使用 redux 从商店中提取值并将它们作为props返回到您的组件的状态

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

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

下面是一个代码片段,它从你的组件中删除状态并依赖于从 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);

在您的情况下,您将需要componentWillReceiveProps并且必须在收到新props时更新状态。因为

  • 在您的构造函数中,您已如下声明您的状态。如您所见,您state使用传入的props构建您的作品。(这就是您需要的原因componentWillReceiveProps以及在那里更新它的逻辑)

    this.state = {
        trajects: props.trajects,
        onClick: props.onClick
    };
    
  • 所以当你的props,变化componentWillReceiveProps是被调用的函数。构造函数不会被调用。因此,您必须再次设置状态,以便更改进入组件的状态。

  • 但是,您的逻辑应如下所示。带有一个条件,以便您可以在多次调用时防止重复的状态更新。

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

只有当你要修改它的内容时,才应该将 props 存储到 state 中。但在你的情况下,我看到没有修改。所以可以直接直接使用this.props.trajects,而不用存入状态再使用。这样你就可以摆脱componentWillReceiveProps 这样你的渲染函数将使用如下所示

{this.props.trajects.map(traject => //what ever is your code.... }
谢谢黑豹,你的解释很清楚!我确实计划扩展组件并允许修改某些道具,因此我将使用您的示例!
2021-06-10 11:49:44

我有类似的问题添加withRouter()如下:

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

您实现的问题在于您将 Redux 存储状态(来自props)复制到您的 React 状态(this.state)

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

由于您在渲染方法中使用状态而不是props,因此您必须手动更改组件的状态this.setState以触​​发渲染。

这不是一个好的模式:我建议不要使用状态,而直接使用这样的props:

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)