React 是否保持状态更新的顺序?

2023-12-21

我知道React可能会异步批量执行状态更新以优化性能。因此,您永远不能相信调用后状态会更新setState。但你能相信React吗按照与以下相同的顺序更新状态setState叫做 for

  1. 相同的组件?
  2. 不同的组件?

考虑单击以下示例中的按钮:

1.有没有可能a 为假,b 为真 for:

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 为真 for:

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:

但是你能相信 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 中所做的事情。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React 是否保持状态更新的顺序? 的相关文章

  • Haxe for javascript 没有全局命名空间污染?

    此问题仅适用于 Haxe 版本 我知道 haxe 一段时间了 但直到昨天才真正使用过它 出于好奇 我决定移植对决 js http wmd editor com examples splitscreen 一个 javascript 端口降价
  • 为什么“true && () => {}”会产生“Uncaught SyntaxError:格式错误的箭头函数参数列表”? [复制]

    这个问题在这里已经有答案了 下面的代码 执行时 true gt yields Uncaught SyntaxError Malformed arrow function parameter list Why 编辑 我知道将函数包装在括号中是
  • PHP 中的 JS charCodeAt 等效项(具有完整的 unicode 和 emoji 兼容性)

    我在 JS 中有一个简单的代码 如果涉及特殊字符 我无法在 PHP 中复制它 这是 JS 代码 参见JSFiddle https jsfiddle net h8oca3qg 5 用于输出 var str t char t and speci
  • 链接 getElementById

    我一直在寻找这个问题的答案 但找不到答案 所以我想尝试一下 StackOverflow 在 javascript 中 这是否有效 x document getElementById myId y x getElementById mySec
  • 通过单击堆叠条形图打开选项卡

    我正在使用 R 构建一个包含转发的堆积条形图 ggplot and plotly 如果单击条形图的一部分 我希望打开一个新的浏览器选项卡并显示该特定日期的推文以及指定的转发量 但是 当我单击下面示例中的其中一个栏时 会打开一个不同的链接 表
  • 如何在D3节点中放置图像?

    到目前为止 我已经创建了这些 D3 节点 用于创建可折叠的层次树 到目前为止 这些节点的颜色为 AA1C1C 深红色 以表明如果您单击它们 它们将扩展到更多节点 我想要做的是在节点中使用图像中的位置 这对于所有用户来说都是一个加号 以知道它
  • javascript中.match和.test有什么区别[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 在浏览 JavaScript 时 我刚刚遇到了 match test 和 exec有什么不同 这是最快的 首先 exec and test
  • console.log 是如何工作的?

    第一个例子 在以下示例中 http jsfiddle net maniator ScTAW 4 http jsfiddle net maniator ScTAW 4 我有这个js var storage function var store
  • 在 Fabric.js 中进行裁剪的“toDataURL”函数中,Multiplier 属性无法正常工作

    我的原始尺寸canvas is 800X700 我在用clipTo要在画布的选定部分中工作 var rect new fabric Rect left 100 top 50 fill fff width 376 height 602 str
  • 获取 Firebase AngularFire 中项目的索引 ID

    这里提出了类似的问题 但接受的答案并没有真正回答这个问题 使用 AngularFire 是否可以创建关系型数据库 或者访问 UniqueID https stackoverflow com questions 16879484 using
  • 代理阻止网络套接字?如何绕行

    我有一个用 Python 编写的正在运行的 websocket 服务器 来自https github com opiate SimpleWebSocketServer https github com opiate SimpleWebSoc
  • 如何查明在 Chrome 控制台中按下按钮时会调用哪些函数?

    我正在尝试自学 Google Closure javascript 库 我正在检查 TreeControl UI 小部件 如何使用Chrome控制台分析当我点击下面演示中的 剪切 按钮时运行了哪些功能 例如 我可以为此设置一个断点吗 我尝试
  • 使用Doctype让scrollTop返回0,为什么?

    当我将此 Doctype 放入我的文档中时document body scrollTop返回零 这是为什么 当您使用该 Doctype 时 您会将每个当前浏览器放入所谓的几乎标准模式 http hsivonen iki fi doctype
  • 如何使 Meteor 上的服务器可以访问文本文件

    我很惊讶我无法在这里搜索我的答案 似乎没有其他人遇到这个问题 当您运行meteor服务时 js html等被打包在 meteor local build文件夹中 但它似乎排除了不是js或html的内容 我有一个名为 magicsets 的文
  • queue.js 是如何工作的?

    我一直试图理解如何Mike Bostock 的queue js https github com mbostock queue blob master queue js有效 但我看不出它是如何工作的 我不明白的部分是代码如何设法继续执行回调
  • 掩码输入数字 - 百分比

    如何通过 jQuery 创建具有百分比的数字掩码输入 我是否让输入仅接受三个数字 并在用户完成输入时在数字后添加百分号 keyup 我不使用插件 例子 1 Or 30 Or 99 Or 100 Or 200
  • 如何控制谷歌地图标记的不透明度

    我需要根据时间使一些标记半透明 有什么方法可以控制标记的 CSS 不透明度吗 或者是否可以可靠地找出标记的 DOM 元素 我使用 Google 地图 API v3 标记的不透明度可以设置为marker setOptions opacity
  • 在 Nest.js 中发送之前如何格式化响应?

    我按照文档进行操作 并能够添加用于响应映射的拦截器 我想要一致的 json 格式输出作为响应 我怎样才能用拦截器或其他比这种方法更好的方法来实现这一点 statusCode 201 message Custom Dynamic Messag
  • 如何从 vue 组件调用 App.vue 中的方法

    我有一个 vue 组件和一个 vue 元素声明 如下所示 Vue component todo item template li This is a todo li methods test function I am getting an
  • 获取css规则、chrome扩展

    我正在开发 Chrome 扩展程序 它需要访问document styleSheets cssRules 它在某些网站上运行良好 例如w3school 但其他人则不然 比如堆栈溢出 我收到错误 Failed to read the cssR

随机推荐

  • Flash 内容的 IE7 z-index 问题

    我遇到了 IE7 中的 Flash 内容始终位于菜单项上方的问题 我有如下结构 div div div ul li foo li li bar li ul div 现在 菜单的最后一项打开 它显示在 Flash 内容后面 摩天大楼位于页面内
  • Jquery阅读文章内容之间的位置进度

    在这个例子中http jsfiddle net SnJXQ 61 http jsfiddle net SnJXQ 61 阅读进度指示器 但它的宽度从网站顶部开始增加 但我们需要进度条宽度开始增加 当文章内容 div 到达文章内容末尾时 这是
  • Google Cloud Datastore 与 Google App Engine

    我正在查看新的 Google Cloud Datastore 看起来很棒 但有一些我无法理解的事情 它应该取代 Google App Engine 数据存储吗 如何在 GAE 中使用它 他们两者有什么区别呢 我有一个 Java 中的 GAE
  • 更新 Google App Engine 上数据存储区中的大量实体

    我想对特定类型的所有实体执行一个小操作并将它们重写到数据存储中 我目前有 20 000 个此类实体 但想要一个可以扩展到任意数量的解决方案 我有什么选择 Use a mapper http code google com p appengi
  • 如何在Chrome开发工具中返回当前调试位置?

    我不断发现代码在 Chrome 调试器中的断点处停止的情况 然后当我使用 控制台 等另一个选项卡并返回 源 时 我丢失了代码停止的位置 我必须单击单步 回到正轨 但这会跳过所寻求的断点 有没有办法到达调试器停止的地方 您可以通过单击调用堆栈
  • 如何添加/设计回调函数

    如何在 C 中设置 注册回调函数 以便在从队列中读取数据时调用函数 Edit 1 使用尼尔的答案作为完整的答案 在头文件中 include
  • 如何在 Android 中读取文本文件? [复制]

    这个问题在这里已经有答案了 我想从文本文件中读取文本 在下面的代码中 发生了异常 这意味着它转到catch堵塞 我将文本文件放在应用程序文件夹中 我应该把这个文本文件 mani txt 放在哪里才能正确读取 try InputStream
  • 必须为此操作提供 PartitionKey 值

    我正在尝试从 Azure Cosmos Db 集合中检索文档 我遇到了错误 Servlet service for servlet dispatcherServlet in context with path threw exception
  • Gson Long 从 String 解析时丢失数据

    我有一些对象的 json 字符串表示 类对象是 public class SMPBBaseObjectsList public ArrayList data new ArrayList lt gt public Integer count
  • 带资源字典的wpf类库

    WPF 类库程序集 dll 中是否可以有资源字典 我问这个问题是因为我有一个 应用程序 WPF 程序集 exe 如果我尝试将程序集的类型转换为 WPF 类库 我会收到错误 无法在库的项目文件中指定元素ApplicationDefinitio
  • 在 Laravel 5.4 中执行批量删除语句时,如何根据 Eloquent 的“删除”事件执行操作?

    我的项目中有 2 个 Eloquent 模型 App StoredFile and App StoredImageSize The StoredFile模型负责保存上传的文件信息 如果上传的文件是图像 则 2 个缩略图将存储在存储中 它们的
  • Ruby On Rails 3,form_tag 远程响应 Ajax 请求

    感谢您阅读这篇文章 过去几天我一直被 RoR 的问题困扰 我在index html erb下有一个表格 h3 Add to list using Ajax h3 Enter the public url
  • Android M - GoogleAccountCredential setSelectedAccount 不起作用 - 名称不得为空

    关于 Android M 预览版GoogleAccountCredential setSelectedAccount似乎不起作用 调试时 我注意到调用该方法后 selectedAccount and accountName对象的字段仍然为空
  • 服务器迁移后图像文件名中的特殊字符编码

    我已将 WordPress 网站从 Hostgator 共享主机迁移到 Ubuntu Digital Ocean LAMP 堆栈 当我导出带有特殊字符的图像文件时 问题就开始了 例如文件oper rios tarsila 1024x640
  • Leaflet/Mapbox渲染问题(灰色区域)

    由于某种原因 我的地图上有一个很大的灰色区域 直到我移动它使其出现 但乍一看 有一个部分缺失了 我以前在网上见过很多这样的情况 但直到现在才想知道如何解决它 这是一个使用 MarkerClusterGroup 作为集群的简单 Mapbox
  • 为什么这个Python键盘中断不起作用? (在 PyCharm 中)

    在 PyCharm 中调试代码时按下 Ctrl C 时 我的 Python try except 循环似乎不会触发键盘中断 在运行程序时使用 Ctrl C 会出现同样的问题 但在 PyCharm Python 控制台中不会出现 我的代码如下
  • TS7006:参数“事件”隐式具有“任意”类型

    在有角度的情况下 这是脚本
  • 如何在asp中发送和处理Http Post?

    httpRequest Open POST www example com handle asp False httpRequest setRequestHeader Content Type application x www form
  • 如何在SQL中计算运行余额

    我正在使用嵌入式德比数据库 我想通过计算借方和贷方金额来添加运行余额列 所以请告诉我解决方案代码和图像也可以在下面找到 正如您在图像平衡总计中看到的那样 不准确 SELECT V DATE FLAG V NUM V NARATION sum
  • React 是否保持状态更新的顺序?

    我知道React可能会异步批量执行状态更新以优化性能 因此 您永远不能相信调用后状态会更新setState 但你能相信React吗按照与以下相同的顺序更新状态setState叫做 for 相同的组件 不同的组件 考虑单击以下示例中的按钮 1