什么?React19暂停发布了

2024-08-02
12215 分钟
...

在 4月25日 React 发布了 19RC 版本(发布主版本前的最后一步),而根据 React 核心团队成员 Joe Savona 的描述,React 出现了问题因此停止了 19 的发布直到他们找到一个合理的修复方式:

那发生了什么呢?

在 React18 中,当 <Suspense> 包裹多个 children 时,当第一个组件抛出了 promise 时,React 会继续渲染这个组件的兄弟节点。而 React 团队认为这是非常影响效率的,因为当抛出了 promise 之后意味着一定会展示 fallback,此时渲染兄弟节点是浪费性能的,因此在 React19 中这个行为被改变了,会立即展示 fallback,等到 promise 被 resolve 之后才会继续渲染兄弟节点。

React19 的重大失误

让我们来看一个实际的例子吧。

比如我们现在在 <Suspense> 中包裹了多个 <Display>组件,每个组件会发起请求拿到对应 id 的数据:

<Suspense fallback={<div>loading</div>}>
  <Display id={1} />
  <Display id={2} />
  <Display id={3} />
  <Display id={4} />
  <Display id={5} />
  <Display id={6} />
</Suspense>

然后我们来看一下 React18/19 的行为有什么不同:

React19

点击查看 Demo:codesandbox.io/p/sandbox/r…

对应 Waterfall:

React18

点击查看 Demo:codesandbox.io/p/sandbox/r…

可以看到更快的展示了内容,对应 Waterfall:

我们可以看到在 React19 时 Waterfall 是顺序的,也就是说后续的请求会在上一次请求完成后被发出,而我们看 React18 的 Waterfall 可以发现请求是同时被发出的。

也就是说从 React18 升级到 React19 之后,这意味着这会对你的应用造成性能问题。

我们可以看一下相关的改动 PR:github.com/facebook/re…

核心改动在 packages/react-reconciler/src/ReactFiberWorkLoop.js 其它大部分都是测试文件。

在原先当抛出错误之后会被 try-catch 捕获,然后在 handleThrow 中会更新 workInProgressSuspendedReason 值:

之后会走到 unwindSuspendedUnitOfWork,这里其实就是正常的流程了,也就是以 DFS 的方式继续遍历兄弟节点,各个 Fiber Node 通过 child、return、sibling 来关联:

然后当回到 Suspense 节点,并且在这个 Fiber Node 上进行标记,代表说现在应该去渲染 Suspense 的fallback 了,这样在 React18 中抛出 promise 的兄弟节点也有机会得到渲染。而在上面的 PR 中我们可以看到当 React 捕获到错误后会从当前节点直接向上寻找 Suspense:

do {
  
  const next = unwindWork(current, incompleteWork, renderLanes);
  
  if (next !== null) { 
    workInProgress = next;
    return;
  }

  const returnFiber = incompleteWork.return; 
  
  
  
  incompleteWork = returnFiber;
} while (incompleteWork !== null);

也就是说取消了渲染兄弟节点的过程,而是直接向上找,直到找到 Suspense,并从 Suspense 开始渲染。

如何解决

社区的一个建议是是否渲染兄弟节点还是直接渲染 Suspense fallback 应该是一个可选择的事情,比如我们可以向 Suspense 加入 strategy={"parallel" | "sequential"} 选项来动态的控制这种行为。

另外,在 RC 版本发布前 React 要经历很多阶段 experimental -> cancary -> beta -> next -> rc -> latest,而这个问题在 rc 之前并没有被社区发现,这使得 React 团队不得不思考如何让更多开发者使用起来从而获得更多反馈,而不仅仅为社区的一些框架比如 nextjs(狗头)服务。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
react2025-12-02
useEffect与useLayoutEffect对比
在 React Hooks 中, 和 都用于处理副作用逻辑,例如订阅、事件监听、DOM 操作等。但它们有着本质性的执行时机差异,这影响到页面渲染、布局抖动、性能表现等关键点。 1\. 执行时机的核心区别 在 React 的渲染流程中: 1. 渲染(Render phase) React 根据 state/props 计算 UI,生成虚拟 DOM,不访问真实 DOM。 2. 提交(Commit phase) React 将虚拟 DOM 变...
学习
react2025-08-03
在 React 中实现倒计时功能会有什么坑
倒计时 倒计时是一个非常常见的业务场景,但是在 React 中实现起来,却不算简单。 首先我们来看这段倒计时代码,它能否正常执行? 来看看实际表现效果 可以看到计时器在不断执行,但是 的值却没有变。 这是一个很经典的问题:React 闭包陷阱 。 我们来详细分析下: 的依赖数组 是空的,这意味着 effect 只会在组件挂载时执行一次,而不会在 状态更新时重新执行。所以 回调函数中捕获的 值始终是初始值 。 这个问题解决起来也很简单,有...
学习面试
react2025-07-30
React性能优化三剑客:memo、useMemo和useCallback详解
前言 在React开发中,性能优化是一个永恒的话题。今天我们就来深入探讨React提供的三个重要性能优化工具: 、 和 ,它们如何帮助我们构建更高效的React应用。 1\. 为什么需要性能优化? 🤔 React的核心机制是当组件的state或props发生变化时,组件会重新渲染。但有时这种重新渲染是不必要的: 父组件更新导致所有子组件重新渲染 ,即使子组件的props没有变化 复杂计算在每次渲染时重复执行 ,消耗大量资源 函数引用在...
学习面试
react2025-06-05
React 中 useDeferredValue 和 startTransition 的核心区别与使用场景
React 中 和 ,两者都用于优化性能,但适用场景和实现方式不同。 核心概念解析 1\. useDeferredValue:延迟值更新 作用 :告诉 React 延迟更新某个值 ,直到所有高优先级渲染任务完成。 适用场景 :当某个值的更新会触发计算密集型渲染(如复杂列表、图表),但需要优先保证其他高优先级 UI(如输入框)的响应。 机制 : 保持旧值显示,后台计算新值。 计算完成后,用新值更新 UI,期间允许用户继续交互。 示例 :...
学习
react2024-12-18
Antd 样式覆盖
&nbsp; 目前作者所在的业务正在升级 Antd5.0,不得不说 Antd5.0 真的太香了,但是由于团队的 UE 规范进行了大改版,Antd5.0 所有的 必须对齐最新的规范,因此就需要对 Antd5.0 组件做主题定制。 现状 目前针对项目中使用的 antd 高频组件进行了梳理,并且根据规范基于 design token 对这些高频组件进行样式定制,对于主题色、圆角、边框、字体等用户 高的主题都已经满足团队规范。 虽然 Desig...
学习
react2024-12-06
React 19 终于发布,一大波新功能正式升级
这篇文章主要介绍了 React 19 的新功能,包括 Actions 自动处理数据突变相关状态,新增的 useActionState、useFormStatus、useOptimistic 等 hook,新的 use API 读取渲染资源,新的 React DOM 静态 API 生成静态站点,以及 React 服务器组件中的 RSC 和 RSA 等内容。同时提到后续会分享针对旧版功能的优化和改良。 关联问题: React 19 性能如何...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录