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 是否保持状态更新的顺序? 的相关文章

  • 我们在哪里/什么时候使用 JSON?

    你能告诉我 JSON 有什么用 在 Javascript 和 PHP 中 当我们需要 JSON 方法时 我从以下链接中阅读 但是 我没有获得有关任何项目的 JSON 实现的任何信息 http www json org js html htt
  • firebase.storage() 不是玩笑测试用例中的函数

    我正在使用 Jest 来测试我的 firebase 功能 这一切都在浏览器中进行 因此我与服务器端的 firebase 没有任何冲突 当我使用firebase auth or firebase database 一切正常 当我尝试使用时fi
  • AngularJS 和 Apiary.IO - 无法读取任何响应标头?

    我使用 Apiary io 模拟我的 API 但不知怎的 我无法使用 angularJS 从响应对象中读取任何标头 我确信我至少通过检查 firebug 正确设置了 Content Type application json Angular
  • 在 Node.js 中实现服务器发送事件的简单方法?

    我环顾四周 似乎在 Node js 中实现 SSE 的所有方法都是通过更复杂的代码 但似乎应该有一种更简单的方法来发送和接收 SSE 是否有任何 API 或模块可以让这件事变得更简单 这是一个每秒发送一个服务器发送事件 SSE 的 Expr
  • 移动浏览器上的 Javascript / jQuery 页面更改事件

    我正在设计一个移动网站 同时考虑所有领先的浏览器 Safari Chrome Dolphin Opera 我想显示一个 正在加载 元素页面导航 更改 请求新页面 我无法在锚标签上使用点击事件 因为有很多锚标签存在preventDefault
  • Google javascript 登录 api:无法离线访问

    我正在尝试为服务器端应用程序实现 Google 登录 如 Google 文档中所示 服务器端应用程序的 Google 登录 https developers google com identity sign in web server si
  • 如何在 JavaScript 中检查未定义的变量

    我想检查变量是否已定义 例如 以下内容会引发未定义的错误 alert x 我怎样才能捕获这个错误 在 JavaScript 中 null是一个对象 不存在的事物还有另一种价值 undefined DOM 返回null对于几乎所有无法在文档中
  • JSSOR - 无法读取未定义的类型属性“currentStyle”

    我正在尝试将 Jssor 滑块实现到我的页面中 但我不断在标题中出现该错误 我的内容是通过 Javascript 动态创建的 如下所示 var slide app createHTML div id inventorySlides null
  • 递归链接 Promise

    我正在开发一个简单的 Windows 8 应用程序 我需要在其中从网站获取一组数据 我正在使用 WinJS xhr 来检索此数据 它返回一个 Promise 然后 我将回调传递到此 Promise 的 then 方法中 该方法为我的回调提供
  • Java:从 ScriptEngine javascript 返回一个对象

    我正在尝试使用 Java 来评估 javascript脚本引擎 https docs oracle com javase 7 docs api javax script ScriptEngine html班级 这是我正在尝试做的事情的一个简
  • 字符串化 JavaScript 对象

    我正在寻找字符串化一个对象 我想要这样的输出 1 valeur dalebrun usager experttasp date 2013 08 20 16 41 50 2 valeur test usager experttasp date
  • JQuery 验证不起作用

    我有一种表单 其中一个输入类型的值为 名字 但这可以在 onfocus 函数上更改我想验证此输入字段 如果它为空白或 名字 我有两个 jQuery 文件jquery 1 4 2 min js jquery validate pack js
  • 如何在 Web 应用程序中使用 Javascript 安全地访问 Windows Azure 移动服务?

    我需要一本 web javascript 安全入门书 根据如何使用 Windows Azure 移动服务的 HTML JavaScript 客户端 http www windowsazure com en us develop mobile
  • 谷歌浏览器不显示一个网站的alert()弹出窗口

    我正在开发一个 javascript 循环 该循环会随着循环的进行而提醒每个键值 为了加快速度 我选中了 阻止此页面创建其他对话框 框 通常这只会抑制一个例程的弹出窗口 但它们还没有回来 在 Google Chrome 中 alert 消息
  • 关于 Node.js Promise then 和 return?

    我对承诺感到困惑 I use 那么就答应没有返回像这样 new Promise resolve reject gt resolve 1 then v1 gt console log v1 new Promise resolve reject
  • 您最喜欢的 JS/CSS 下拉菜单是什么? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 希望在网站上实现一个 只是好奇其他人都使用过什么以及他们有过什么样的体验 EDIT我也不是一个超级粉丝
  • 如何设置 .eslintrc 来识别“require”?

    我是新来的ESLint http eslint org 并且我已经成功地将 ESLint 与IntelliJ https www jetbrains com idea 开箱即用 我的 ESLint 集成无法识别node 但对文档的基本审查表
  • Recoil 中的动态原子键

    我正在尝试创建一个动态表单 其中表单输入字段是根据 API 返回的数据呈现的 由于atom需要有一个唯一的键 我尝试将它包装在一个函数中 但是每次我更新字段值或重新安装组件 尝试更改选项卡 时 我都会收到一条警告 我在这里做了一个小的运行示
  • 如何连接/组合两个数组以连接成一个数组?

    我正在尝试将 JavaScript 中的 2 个数组合并为一个 var lines new Array a b c lines new Array d e f 这是一个简单的例子 我希望能够将它们组合起来 这样当读取第二行时 数组中的第四个
  • nodeJS exec 不适用于“cd”shell cmd

    var sys require sys exec require child process exec exec cd home ubuntu distro function err stdout stderr console log cd

随机推荐

  • 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