React 18 并发更新:高优先级先渲染,为什么最终状态不会算错?

React 18 的并发渲染有一个很容易被误解的地方:高优先级更新可以先参与渲染,但这不代表 React 会永久打乱状态更新的顺序。

更准确地说,在使用 Transition 等并发特性时,低优先级渲染可以被更紧急的更新打断。React 可能先把高优先级结果展示出来,再回头处理之前跳过的低优先级更新。不过到了最终计算阶段,它仍然要保留更新之间原本的依赖关系。

这也是为什么页面可能出现 1 → 3 → 9,而不是简单地得到 5

先看一个例子

假设初始状态是 1,先入队一个低优先级的"加 2",随后又入队一个普通的"乘 3":

jsx 复制代码
import { startTransition, useState } from 'react';

function Demo() {
  const [value, setValue] = useState(1);

  function handleClick() {
    startTransition(() => {
      setValue(value => value + 2); // 低优先级,先入队
    });

    setValue(value => value * 3); // 更紧急,后入队
  }

  return <button onClick={handleClick}>{value}</button>;
}

如果完全按照入队顺序计算,结果应该是:

ini 复制代码
(1 + 2) * 3 = 9

但高优先级更新需要更快响应。那 React 能不能直接把"乘 3"挪到前面,变成下面这样?

ini 复制代码
(1 * 3) + 2 = 5

不能。因为这会改变更新原本的语义。

高优先级先展示,不等于永久改变计算顺序

React 的更新队列不是按优先级重新排序的,新更新仍然按照入队顺序追加。优先级真正影响的是:当前这轮渲染要处理哪些更新。

在一次可能的调度过程中,计算会变成这样:

阶段 React 做了什么 结果
初始状态 页面已经提交 1
紧急更新渲染 暂时跳过低优先级的"加 2",先执行"乘 3" 3
后续低优先级渲染 从保留的基础状态重新计算,依次执行"加 2"和"乘 3" 9

所以,页面有可能呈现:

复制代码
1 → 3 → 9

关键点在于,低优先级更新被跳过时并没有被丢掉。React 会保留当时的基础状态和后续更新。等低优先级任务重新开始时,后面的高优先级更新也会一起被重放,也就是常说的 rebase。

这样既能让紧急更新尽快出现在页面上,又能保证最终结果仍然等价于按入队顺序执行:

ini 复制代码
(1 + 2) * 3 = 9

中间状态不是固定承诺

1 → 3 → 9 只是一种可能观察到的提交顺序,不是每次运行都会稳定出现的动画过程。

React 官方对并发渲染的定义本身就允许暂停、继续,甚至放弃一轮尚未完成的渲染。设备性能、任务耗时以及后续是否又来了更紧急的更新,都会影响中间结果有没有机会真正提交到页面。

因此可以把它理解成:

  • 页面不保证展示队列里的每一个中间状态。
  • 更新之间的计算依赖仍然按照入队顺序保留。
  • 在更新函数纯净、更新序列相同的前提下,优先级可以影响中间展示,但不应该改变最终结果。

写业务代码时要注意什么

第一,如果下一次状态依赖上一次状态,使用函数式更新:

jsx 复制代码
setValue(value => value + 2);

不要把这类例子直接写成:

jsx 复制代码
setValue(value + 2);

后者拿到的是当前这次渲染里的状态快照,表达的已经不是"基于队列中的上一个状态继续计算"了。

第二,更新函数必须保持纯净。并发渲染可能重启,跳过的更新也可能在 rebase 时被重新执行。如果在更新函数里修改外部变量、发送请求或者做其他副作用,重放时就可能产生意料之外的结果

第三,不要让业务逻辑依赖某个中间状态一定会显示。React 保证的是提交后的 UI 一致性和最终计算结果,不是每一轮尝试渲染都能被用户看到。

总结

整体看下来,React 18 并不是把状态更新按优先级重新排个序,然后从头算一遍。

它做的是:紧急的先处理,暂时处理不了的先留下;等后面继续时,再从正确的基础状态把更新按原顺序接回去。页面中间可能先看到 3,最终仍然会回到 (1 + 2) * 3 = 9 这条计算链路上。

相关推荐
tedcloud1231 小时前
diagram-design 怎么安装?用 AI 自动生成更专业的架构图、流程图
linux·运维·前端·人工智能·开源·流程图
前端小万1 小时前
两个半月的时间如何赚到 3000 块钱。
前端
YIAN1 小时前
端侧大模型:DeepSeek-R1 WebGPU 推理全流程源码深度解析
前端·typescript·deepseek
请你吃div1 小时前
个人 Nuxt4 博客 SEO 优化实战教程
前端·nuxt.js·seo
网安蟹佬霸1 小时前
Android安全攻防实战:从APK逆向到Frida动态Hook全流程详解(附脚本)
android·前端·安全·web安全·逆向·csrf·网安
计算机魔术师1 小时前
芯片不跌反涨?两家公司的赌局正在重塑全球算力格局
前端
IT_陈寒1 小时前
Python多进程池的坑:子进程竟然不会退出
前端·人工智能·后端
snow@li1 小时前
前端:全景深度分析/前端岗位起源,以及“美国没有前端岗位”的真相
前端
恋猫de小郭1 小时前
Gradle 9.7.0 将提速 Android 构建,Sync 提升接近一倍
android·前端·flutter