我可能错过了一些东西。我知道setState
React 中是异步的,但我似乎仍然有这个问题。
想象一下,当用户单击我的应用程序上的任何按钮时,下面是一个处理程序
1. ButtonHandler()
2. {
3. if(!this.state.flag)
4. {
5. alert("flag is false");
6. }
7. this.setState({flag:true});
8.
9. }
- 现在想象一下,用户非常快速地单击第一个按钮,然后单击第二个按钮。
- 想象一下,第一次调用处理程序
this.setState({flag:true})
执行时,但是当第二次调用处理程序时,尚未反映上一次调用的状态更改- 并且this.state.flag
返回false
。 - 这种情况会发生吗(甚至在理论上)? 有哪些方法可以确保我阅读最新的状态?
- 我知道
setState(function(prevState, props){..})
可以让您访问以前的状态,但是如果我只想像第 3 行那样读取状态而不设置它怎么办?
正如您正确指出的,要根据以前的状态设置状态,您需要使用函数重载。
我知道setState(function(prevState, props){..})让你访问以前的状态
所以你的例子看起来像这样:
handleClick() {
this.setState(prevState => {
return {
flag: !prevState.flag
};
});
}
如果我只想像第 3 行一样读取状态而不设置它怎么办?
让我们回过头来思考为什么要这样做。
如果要执行副作用(例如.log,控制台或启动 AJAX 请求),那么正确的位置是componentDidUpdate
生命周期方法。它还允许您访问以前的状态:
componentDidUpdate(prevState) {
if (!prevState.flag && this.state.flag) {
alert('flag has changed from false to true!');
}
if (prevState.flag && !this.state.flag) {
alert('flag has changed from true to false!');
}
}
这是使用 React 状态的预期方式。你让 React 管理状态,而不用担心它何时被设置。如果要根据以前的状态设置状态,请将函数传递给setState
。如果要根据状态更改执行副作用,请在componentDidUpdate
中比较以前和当前状态。
当然,作为最后的手段,您可以使实例变量独立于状态。
React 的哲学
状态和 props 应指示组件渲染所需的内容。每当状态和道具发生变化时,都会调用 React 的渲染。
副作用
在您的情况下,您正在根据需要特定时间的用户交互引起副作用。在我看来,一旦你退出渲染 - 你可能想要重新考虑state
和props
,并坚持使用无论如何都是同步的常规实例属性。
解决真正的问题 - 在 React 之外
只需将this.state.flag
更改为随处this.flag
,并使用分配而不是setState
更新它。这样你
如果您仍然必须使用 .state
你可以绕过这个,丑陋的。我为此编写了代码,但我宁愿不在这里发布它,这样人们就不会使用它:)
- 首先承诺。
- 然后使用实用程序只关心函数调用中解析的最后一个承诺。这是一个示例库,但实际代码是~10LoC,无论如何都很简单。
- 现在,一个带有
last
调用的 promisized setState 为您提供了您正在寻找的保证。
以下是使用此类代码的方式:
explicitlyNotShown({x: 5}).then(() => {
// we are guaranteed that this call and any other setState calls are done here.
});
(注意:对于 MobX,这不是问题,因为状态更新是同步的)。