正确使用React getDerivedStateFromProps

时间:2018-05-25 15:32:09

标签: reactjs

在这个例子中

https://codepen.io/ismail-codar/pen/QrXJgE?editors=1011

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("nextProps", nextProps, "\nprevState", prevState)
    if(nextProps.count !== prevState.count)
      return {count: nextProps.count};
    else
      return null;
  }
  handleIncrease(e) {
    this.setState({count: this.state.count + 1})
  }
  handleDecrease(e) {
    this.setState({count: this.state.count - 1})
  }
  render() {
    return <div>
      <button onClick={this.handleIncrease.bind(this)}>+</button>
      {this.state.count}
      <button onClick={this.handleDecrease.bind(this)}>-</button>
    </div>;
  }
}

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = { initialCount: 1 };
  }
  handleChange(e) {
    this.setState({initialCount: e.target.value})
  }
  render() {
    return <div>
      <Counter count={this.state.initialCount} />
      <hr/>
      Change initial:<input type="number" onChange={this.handleChange.bind(this)} value={this.state.initialCount} />
    </div>
  }
}

ReactDOM.render(
  <Main/>,
  document.getElementById("root")
);

预期: 单击+ / - 按钮和文本框更改必须是更新计数

目前: 主组件将initialCount存储在自己的状态,并将初始计数传递给子计数器组件。

如果从textbox触发handleChange并且更新了initialCount,则子计数器组件也会正确更新,因为getDerivedStateFromProps静态方法提供了这个。

但是改变计数器组件中的计数值,通过handleIncrease和handleDecrease方法更新本地状态就可以了。

问题是getDerivedStateFromProps重新触发此次并重置计数值。但我没想到这是因为Counter组件本地状态更新父主组件没有更新。 UNSAFE_componentWillReceiveProps正在以这种方式工作。

摘要我的getDerivedStateFromProps用法不正确,或者我的方案有另一种解决方案。

此版本https://codepen.io/ismail-codar/pen/gzVZqm?editors=1011适用于componentWillReceiveProps

2 个答案:

答案 0 :(得分:6)

尝试&#34;同步&#34;像你这样的道具状态极易出错,导致错误的应用程序。

事实上即使你的example with componentWillReceiveProps也有错误。
如果您更频繁地重新渲染父组件,则会丢失用户输入。

这是demo of the bug。 增量计数器,然后单击“演示bug”,它将吹走计数器。但是那个按钮的setState应该是完全不相关的。

这显示为什么尝试将状态同步到道具是一个坏主意,应该避免

相反,请尝试以下方法之一:

  1. 您可以完全控制您的组件&#34;通过父道具并删除当地的州。

  2. 或者,您可以使您的组件完全“不受控制”,并在必要时通过为孩子提供不同的key来重置父级的子状态。

  3. 这两种方法都在 this article on the React blog about avoiding deriving state 中描述。博客文章包含演示的详细示例,因此我强烈建议您查看它。

答案 1 :(得分:1)

我不确定我是否理解正确,但是如果你想将道具用作种子&#34;对于在构造函数中执行此操作的初始值,您甚至不需要getDerivedStateFromProps。你实际上不需要复制州:

class Counter extends React.Component {
  render() {
    return <div>
      <button onClick={this.props.handleIncrease}>+</button>
      {this.props.count}
      <button onClick={this.props.handleDecrease}>-</button>
    </div>;
  }
}

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 1 };
  }
  handleIncrease() {
    this.setState(prevState => ({count: prevState.count + 1}))  
  }
  handleDecrease() {
    this.setState(prevState => ({count: prevState.count - 1}))  
  }
  render() {
    return (
     <div>
      <Counter count={this.state.count} />
      <hr/>
      Change initial: 
      <input 
        type="number" 
        handleIncrease={this.handleIncrease.bind(this)} 
        handleDecrease={this.handleDecrease.bind(this)} 
        count={this.state.count} 
      />
     </div>
    )
  }
}