并发与 Suspense
并发渲染让 React 可以把“计算下一次 UI”分片处理,并在必要时中断/重试,从而把 CPU 时间更优先地分配给用户正在做的事(输入、点击、滚动)。 Suspense 则让“某块 UI 尚未就绪”成为可组合的结构能力:你可以用边界把未就绪部分隔离,先展示可用部分。
1) 并发到底改变了什么?
并发改变的是 React 的“调度策略”,不是你的组件 API。最重要的心智变化只有一句话:render 只是计算,可能会被打断并重做。 这也是为什么 React 一直强调 render 必须是纯函数式的 UI 计算。
关键不变量
- 不要在 render 阶段触发副作用(网络请求、写 DOM、订阅、打点)。
- 副作用属于 commit 后:用 Effect 表达资源生命周期。
- 把“性能问题”归因到某个 Hook 往往不靠谱,先看更新优先级与边界设计。
2) 优先级:为什么 Transition 不是“防抖”
并发最实用的能力是优先级。用户输入是紧急的;“根据输入算出大结果”通常不是紧急的。 过渡更新(transition)让 React 知道:可以在不影响输入响应的前提下慢慢把结果算出来。
TypeScript
import { useMemo, useState, useTransition } from 'react';
export function SearchList({ items }: { items: string[] }) {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const filtered = useMemo(() => {
const q = query.trim().toLowerCase();
if (!q) return items;
return items.filter((x) => x.toLowerCase().includes(q));
}, [items, query]);
return (
<div>
<input
value={query}
onChange={(e) => {
const next = e.target.value;
startTransition(() => setQuery(next));
}}
placeholder="输入保持响应,结果可以稍后更新"
/>
{isPending && <p>更新中…</p>}
<ul>{filtered.map((x) => <li key={x}>{x}</li>)}</ul>
</div>
);
}更细的契约与边界请对照:useTransition、 useDeferredValue。
3) Suspense:把“就绪度”变成 UI 结构
Suspense 的核心不是 fallback,而是边界:你用边界定义“哪些内容可以先展示、哪些内容可以后到”。 这让渐进式渲染变成一种可组合的结构能力,而不是每个页面手写一套 loading 状态机。
如果你想系统掌握 Suspense 的边界放置、与缓存/SSR 的关系,建议看: Suspense(深挖)。
4) 组合:并发 + Suspense 的“正确目标”
- 把输入/点击保持为紧急更新,让交互永远即时。
- 把大计算、路由切换、数据/代码就绪等当作过渡更新。
- 用 Suspense 把“未就绪”隔离在合理边界内,避免整个页面闪回骨架。
快速自检(工程落地)
- 你的边界是不是从“体验目标”倒推,而不是从组件树随手包一层?
- 你有没有稳定缓存/去重机制来支撑“重试渲染”?
- 你是否为错误提供了可恢复路径(重试/回退/刷新)?
下一步
- Suspense API:/reference/react/suspense
- Transition API:/reference/react/use-transition
- React 19 表单提交体系:/learn/actions
上一页 / 下一页