【问题标题】:Set state based on previous one in render function根据渲染函数中的前一个设置状态
【发布时间】:2020-03-09 15:02:42
【问题描述】:

我最近阅读了 react.js documentation 并发现根据之前的状态值设置状态不一致。这是这段代码:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

我认为设置state 的这种方式() =&gt; this.setState({ count: this.state.count + 1 }) 是错误的,您应该为此使用回调。所以我提出了PR,在其中我添加了对先前状态的回调函数的使用,但它已被弃用,因为

当您不确定捕获的状态值是什么时,回调版本很有用。

我真的不喜欢你不是解释的某些部分,有人可以解释为什么这样做() =&gt; this.setState({ count: this.state.count + 1 }) 设置状态是正确的。

提前致谢。

【问题讨论】:

  • 在这个例子中,你并没有真正面临获得过时状态副本的风险。您只调用一次setState,并且每次渲染该事件只会发生一次。所以似乎不需要使用回调。
  • 我已要求对 PR 进行澄清。这就是这次谈话的地方。
  • @BrianThompson 我不相信在他们自己的例子中反对 React doc 的 own guidelines 是个好主意。
  • @DaveNewton 谢谢
  • Пожалуйста - IMO 你是对的,应该更新示例以保持一致。

标签: javascript reactjs setstate


【解决方案1】:

在您当前的示例中,您将默认状态设置为{ count: 0 },您可以“安全”地执行setState({ count: this.state.count + 1 }),因为当您第一次更新您的状态时,0 + 1 将产生有效结果。

class App extends React.Component {
  state = { count: 0 }
  render() {
    return (
      <div>
         <p>You clicked {this.state.count} times</p>
         <button
           onClick={() => this.setState({ count: this.state.count + 1 })}
         >
           Click me!
         </button>
      </div>
    )
  }
}
ReactDOM.render(
  <App />,
  document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

但是,让我们假设您对某些state 的初始值不是0,因此调用this.state.count + 1 可能会产生无效结果。这是您可以使用callback 版本的地方,因为:

您不确定捕获的状态值是什么。

class App extends React.Component {
  state = { count: null }
  render() {
    const handleClick = () => {
      this.setState((prevState) => {
        // count is initially `null`, so 
        // `null + 1` could yield an undesired result
        if (!Number.isInteger(prevState.count)) {
          return { count: 1 }
        }
        return { count: prevState.count + 1 }
      })
    }
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={handleClick}>
          Click me!
        </button>
      </div>
    ) 
  }
}
ReactDOM.render(
  <App />,
  document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

这只是一个例子,但你明白了。


您的 PR 很可能被拒绝,因为文档中的示例是正确的,假设该示例用于通过执行 this.setState({ count: this.state.count + 1 }) 更新您的状态是“安全”的相同上下文。

这是文档:

两种更新state 的方式都是正确的,应该在适当的时候使用。正如您在第二个示例中看到的那样,如果想要在更新您的 state 之前进行一些检查,“回调选项”将是一个更好的解决方案。

不过,文档中的示例是正确的,如果它改用“回调选项”,则不会产生任何好处。

【讨论】:

  • 顺便说一句,我也可以像这样检查:javascript &lt;button onClick={() =&gt; { if (!Number.isInteger(this.state.count)) { return { count: 1 } } return { count: this.state.count + 1 } } } &gt;
  • @ЯрославТерещук 是的,我想你可以,但这看起来很乱。使用您认为适合您正在做的事情的任何方法,但以上内容应该让您很好地了解您的 PR 被拒绝的原因。
  • 好的,我明白你的意思了,但是我在哪里可以阅读更多关于这方面的信息?例如,我什么时候会出现陈旧状态?
  • @ЯрославТерещук 取决于您在做什么以及组件的复杂程度。我会从reactjs.org/docs/…reactjs.org/docs/react-component.html#setstate 开始,但这取决于React 如何触发更新。如果您尝试访问render 内部的this.state.count,您可能不会进入“陈旧”状态,但如果您的组件上有一个方法试图使用this.state.count 来更新state.count,你可能会遇到问题,所以使用回调。
  • @ЯрославТерещук 如果我能想出一种方法来解决这个问题并展示我在说什么,我会更新这些示例。
【解决方案2】:

使用返回函数

通过在此处传递函数,您将始终可以访问之前的状态。

this.setState((prevState) =&gt; ({ count: prevState.count + 1 }))

请注意,您还可以添加道具作为第二个参数。

如果您想了解有关此回调的更多信息,请查看this article。它非常详细。

【讨论】:

  • 这不是问题; OP 询问为什么 PR 被拒绝。
  • @DaveNewton 啊,好点,我读了标题并回答了这个问题。相当误导
猜你喜欢
  • 2020-03-10
  • 1970-01-01
  • 2020-04-27
  • 2020-02-12
  • 1970-01-01
  • 1970-01-01
  • 2017-07-18
  • 1970-01-01
  • 2019-04-29
相关资源
最近更新 更多