【问题标题】:Proper use of React getDerivedStateFromProps正确使用 React getDerivedStateFromProps
【发布时间】:2018-05-25 15:32:08
【问题描述】:

在这个例子中

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 存储在自己的状态中,并将初始计数传递给子 Counter 组件。

如果从文本框触发的 handleChange 和 initialCount 被更新,子 Counter 组件也会正确更新,因为 getDerivedStateFromProps 静态方法提供了这一点。

但是通过 handleIncrease 和 handleDecrease 方法更新本地状态来更改 Counter 组件中的计数值,这很麻烦。

问题是 getDerivedStateFromProps 这次重新触发并重置计数值。但我没想到这是因为 Counter 组件本地状态更新父 Main 组件没有更新。 UNSAFE_componentWillReceiveProps 就是这样工作的。

总结一下我的 getDerivedStateFromProps 用法不正确,或者我的场景有其他解决方案。

这个版本https://codepen.io/ismail-codar/pen/gzVZqm?editors=1011 好用componentWillReceiveProps

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    像你一样尝试将状态“同步”到 props 非常容易出错,并导致应用程序出现错误。

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

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

    这说明了为什么尝试将状态同步到 props 是一个坏主意,应该避免

    请尝试以下方法之一:

    1. 您可以让您的组件完全由父道具“控制”并移除本地状态。

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

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

    【讨论】:

      【解决方案2】:

      我不确定我是否理解正确,但如果你想在构造函数中使用道具作为初始值的“种子”,你甚至不需要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>
          )
        }
      }
      

      【讨论】:

      • 谢谢托马斯。您的建议是另一种解决方案,但我无法在现实世界中使用它。组件必须使用自己的事件处理程序更新自己的状态。
      猜你喜欢
      • 1970-01-01
      • 2020-05-07
      • 1970-01-01
      • 1970-01-01
      • 2020-12-30
      • 2023-02-01
      • 1970-01-01
      • 2018-10-11
      • 1970-01-01
      相关资源
      最近更新 更多