我知道 React 可能会异步和批量执行状态更新以优化性能。因此,在调用 setState
之后,您永远不能相信状态会被更新。 .但是你能相信 React to 以与 setState
相同的顺序更新状态被称为 为了
考虑单击以下示例中的按钮:
1. 有没有可能 a 为假 b 为真 为了:
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { a: false, b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.setState({ a: true });
this.setState({ b: true });
}
}
2. 有没有可能 a 为假 b 为真 为了:
class SuperContainer extends React.Component {
constructor(props) {
super(props);
this.state = { a: false };
}
render() {
return <Container setParentState={this.setState.bind(this)}/>
}
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.props.setParentState({ a: true });
this.setState({ b: true });
}
}
请记住,这些是对我的用例的极端简化。我意识到我可以以不同的方式做到这一点,例如在示例 1 中同时更新两个状态参数,以及在示例 2 中对第一个状态更新的回调中执行第二个状态更新。但是,这不是我的问题,我只对是否存在感兴趣React 执行这些状态更新的定义明确的方式,仅此而已。
非常感谢任何由文档支持的答案。
最佳答案
我在 React 上工作。
TLDR:
But can you trust React to update the state in the same order as setState is called for
- the same component?
是的。
- different components?
是的。
订购 的更新始终受到尊重。您是否看到它们“之间”的中间状态取决于您是否在批处理中。
在 React 17 及更早版本中,默认情况下,仅对 React 事件处理程序内部的更新进行批处理 .有一个不稳定的 API 可以在您需要的极少数情况下强制在事件处理程序之外进行批处理。
从 React 18 开始,React batches all updates by default . 请注意,React 永远不会从两个不同的有意事件(如点击或打字)批量更新,因此,例如,两个不同的按钮点击永远不会被批量更新。在少数不需要批处理的情况下,您可以使用
flushSync
.理解这一点的关键是 不管有多少
setState()
调用你在 React 事件处理程序中执行的组件数量,它们将在事件结束时仅产生一次重新渲染 .这对于大型应用程序的良好性能至关重要,因为如果 Child
和 Parent
每次通话setState()
在处理点击事件时,您不想重新渲染 Child
两次。在您的两个示例中,
setState()
调用发生在 React 事件处理程序中。因此,它们总是在事件结束时一起刷新(并且您看不到中间状态)。更新始终为 按它们出现的顺序浅合并 .所以如果第一次更新是
{a: 10}
,第二个是{b: 20}
,第三个是{a: 30}
,渲染状态将为{a: 30, b: 20}
.对同一状态键的最新更新(例如,在我的示例中,如 a
)总是“获胜”。this.state
当我们在批处理结束时重新渲染 UI 时,对象会更新。因此,如果您需要根据之前的状态更新状态(例如递增计数器),您应该使用功能 setState(fn)
为您提供先前状态的版本,而不是从 this.state
读取.如果您对此原因感到好奇,我会深入解释它 in this comment .在您的示例中,我们不会看到“中间状态”,因为我们是 在 React 事件处理程序中 启用批处理的位置(因为当我们退出该事件时,React“知道”)。
然而,在 React 17 和更早的版本中,在 React 事件处理程序之外默认没有批处理 .因此,如果在您的示例中我们有一个 AJAX 响应处理程序而不是
handleClick
, 每个 setState()
将在发生时立即处理。在这种情况下,是的,你 会查看 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
时,更新被刷新。称呼。该 API 是“不稳定的”,因为我们最终将在 18 之后的某个主要版本(19 或更高版本)中将其删除。如果您需要在 React 事件处理程序之外的某些情况下强制批处理,您可以安全地依赖它直到 React 18。使用 React 18,您可以删除它,因为它不再有任何效果。
总而言之,这是一个令人困惑的话题,因为默认情况下 React 过去只批处理内部事件处理程序。但解决方案不是批量减少,而是默认批量增加。这就是我们在 React 18 中所做的。
关于javascript - React 是否保持状态更新的顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48563650/