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

时间:2017-09-08 09:24:29

标签: reactjs react-redux

我是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中收到的数据,这是新的新状态。 所以我不明白为什么我需要componentWillReceiveProps函数才能使渲染函数接收新数据。

我做错了吗?

5 个答案:

答案 0 :(得分:39)

如果你想用新的道具值更新状态值,则需要

componentWillReceiveProps,只要道具值发生任何变化,就会调用此方法。

  

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

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

this.state.KeyName

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

如果您不将道具值存储在州中并直接使用,则不需要

componentWillReceiveProps

this.props.keyName

现在,反应将始终在渲染方法中使用更新的道具值,如果道具发生任何变化,它将使用新道具重新渲染组件。

根据 DOC

  

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

     

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

<强>建议:

不要将道具值存储在状态中,直接使用this.props并创建ui组件。

答案 1 :(得分:3)

更新

private Control _parentControl; protected void Page_Load(object sender, EventArgs e) { _parentControl = this; //if using an UpdatePanel use the ID of that instead of 'this' DataForRepeater(); } private void DataForRepeater() { //just example data to load the repeater var dt = new DataTable(); dt.Columns.Add("GroupID", typeof(int)); dt.Columns.Add("GroupName", typeof(string)); dt.Columns.Add("GroupDescription", typeof(string)); for (int i = 1; i <= 10; i++) { var nr = dt.NewRow(); nr["GroupID"] = i; nr["GroupName"] = "SomeName" + i.ToString(); nr["GroupDescription"] = "Description of " + i.ToString() + " item"; dt.Rows.Add(nr); } DataRepeater.DataSource = dt; DataRepeater.DataBind(); } protected void DataRepeater_ItemCommand(object sender, CommandEventArgs e) { string command = e.CommandName; string[] arguments = e.CommandArgument.ToString().Split('|'); switch (command) { case ("Edit"): //edit operation IdOfItemToEditHiddenField.Value = arguments[0]; NameTextBox.Text = arguments[1]; ScriptManager.RegisterStartupScript(_parentControl, _parentControl.GetType(), "Modal", " DisplayEditModal()", true); break; case ("Delete"): //delete operation IdOfItemToDeleteHiddenField.Value = arguments[0]; DeleteMessageLabel.Text = arguments[1] + "<br/>" + arguments[2]; ScriptManager.RegisterStartupScript(_parentControl, _parentControl.GetType(), "Modal", " DisplayDeleteModal()", true); break; default: throw new InvalidOperationException(); } } protected void btnSaveEdit_Click(object sender, EventArgs e) { int idToSave = int.Parse(IdOfItemToEditHiddenField.Value); string newName = NameTextBox.Text; //do something to save it ScriptManager.RegisterStartupScript(_parentControl, _parentControl.GetType(), "Msg", string.Format("alert('Save item {0}: {1}');", idToSave, newName), true); } protected void btnDelete_Click(object sender, EventArgs e) { int idToDelete = int.Parse(IdOfItemToDeleteHiddenField.Value); //do something to delete it ScriptManager.RegisterStartupScript(_parentControl, _parentControl.GetType(), "Msg", string.Format("alert('Delete item {0}');", idToDelete), true); }

现在应该使用

而不是componentWillReceiveProps

还会看到article from gaearon重写弹性组件

这里有两个潜在的问题

  1. 不要重新分配你的道具,说明你正在使用redux来从商店中提取值并将它们作为道具返回到你的组件
  2. 避免状态意味着您不再需要构造函数或生命周期方法。因此,您的组件可以编写为无状态功能组件,以这种方式编写组件会带来性能优势。

    1. 如果您正在传递mapDispatcahToProps,则无需在调度中包装您的操作。如果传递了一个对象,则假定其中的每个函数都是一个动作创建者。将返回具有相同功能名称的对象,但将返回包含在分派中的每个操作创建者
    2. 下面是一个代码片段,它从组件中删除状态并依赖于从redux商店返回的状态

      &#13;
      &#13;
      componentDidUpdate()
      &#13;
      &#13;
      &#13;

答案 2 :(得分:2)

我有类似问题添加withRouter(),如下所示:

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

答案 3 :(得分:1)

您的实现问题是您正在将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)

答案 4 :(得分:0)

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