我从事 React 工作。
TLDR:
但是你能相信 React 会按照调用 setState 的顺序更新状态吗
Yes.
Yes.
The order更新始终受到尊重。您是否看到它们“之间”的中间状态取决于您是否在批次中。
在 React 17 及更早版本中,默认情况下,仅对 React 事件处理程序内的更新进行批处理。在极少数情况下,当您需要时,有一个不稳定的 API 可以强制在事件处理程序之外进行批处理。
从 React 18 开始,React默认情况下批处理所有更新 https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching.请注意,React 永远不会对两个不同的有意事件(例如单击或键入)进行批处理更新,因此,例如,两次不同的按钮单击永远不会进行批处理。在极少数不需要批处理的情况下,您可以使用flushSync https://reactjs.org/docs/react-dom.html#flushsync.
理解这一点的关键是无论有多少setState()
调用你做了多少个组件在 React 事件处理程序中,他们将在事件结束时仅生成一次重新渲染。这对于大型应用程序中的良好性能至关重要,因为如果Child
and Parent
每次通话setState()
处理点击事件时,您不想重新渲染Child
twice.
在你的两个例子中,setState()
调用发生在 React 事件处理程序内部。因此,它们总是在事件结束时刷新在一起(并且您看不到中间状态)。
更新总是有的按照发生的顺序浅层合并。所以如果第一次更新是{a: 10}
,第二个是{b: 20}
,第三个是{a: 30}
,渲染状态将是{a: 30, b: 20}
。对同一状态密钥的最新更新(例如a
在我的例子中)总是“赢”。
The this.state
当我们在批处理结束时重新渲染 UI 时,对象就会更新。因此,如果您需要根据先前的状态更新状态(例如递增计数器),您应该使用函数setState(fn)
为您提供先前状态的版本,而不是从中读取this.state
。如果您对其中的原因感到好奇,我对此进行了深入的解释在这个评论中 https://github.com/facebook/react/issues/11527#issuecomment-360199710.
在您的示例中,我们不会看到“中间状态”,因为我们是在 React 事件处理程序中启用批处理的地方(因为 React“知道”我们何时退出该事件)。
然而,无论是在 React 17 还是更早的版本中,默认情况下,React 事件处理程序之外没有批处理。因此,如果在您的示例中我们有一个 AJAX 响应处理程序而不是handleClick
, each setState()
发生时将立即处理。在这种情况下,是的,你would查看 React 17 及更早版本中的中间状态:
promise.then(() => {
// We're not in an event handler, so these are flushed separately.
this.setState({a: true}); // Re-renders with {a: true, b: false }
this.setState({b: true}); // Re-renders with {a: true, b: true }
this.props.setParentState(); // Re-renders the parent
});
我们意识到这很不方便根据您是否处于事件处理程序中,行为会有所不同。在 React 18 中,这不再是必要的,但在此之前,有一个 API 可以用来强制批处理:
promise.then(() => {
// Forces batching
ReactDOM.unstable_batchedUpdates(() => {
this.setState({a: true}); // Doesn't re-render yet
this.setState({b: true}); // Doesn't re-render yet
this.props.setParentState(); // Doesn't re-render yet
});
// When we exit unstable_batchedUpdates, re-renders once
});
内部 React 事件处理程序都被包装在unstable_batchedUpdates
这就是为什么它们默认是批处理的。请注意,将更新包装在unstable_batchedUpdates
两次没有效果。当我们退出最外层时,更新将被刷新unstable_batchedUpdates
call.
该 API 是“不稳定的”,因为我们最终会在 18 版(19 版或更高版本)之后的某个主要版本中删除它。如果您需要在 React 事件处理程序之外的某些情况下强制批处理,您可以安全地依赖它,直到 React 18。使用 React 18,您可以将其删除,因为它不再有任何效果。
总而言之,这是一个令人困惑的主题,因为 React 默认情况下仅在事件处理程序内进行批处理。但解决办法不是批量少,就是为了更多批次默认情况下。这就是我们在 React 18 中所做的事情。