并发特性
建立在 Fiber 与调度 之上:把更新标成 紧急 / 可延迟,减少输入卡顿。
React 18+ 的 Concurrent 不是「多线程跑 JS」,而是:在主线程上可中断、可优先级调度的渲染。
useTransition:把更新标成「可中断」
典型场景:输入框 要即时回显,同时要根据关键词过滤大列表——列表可以慢一点。
import { useState, useTransition } from 'react'
function Search({ items }: { items: string[] }) {
const [text, setText] = useState('')
const [list, setList] = useState(items)
const [isPending, startTransition] = useTransition()
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
const value = e.target.value
setText(value) // 紧急:输入立刻更新
startTransition(() => {
setList(items.filter((x) => x.includes(value))) // 可延迟
})
}
return (
<>
<input value={text} onChange={onChange} />
{isPending && <span>更新中…</span>}
<ul>
{list.map((x) => (
<li key={x}>{x}</li>
))}
</ul>
</>
)
}
要点:
startTransition里触发的更新优先级更低,可被打断isPending表示过渡中的 UI 状态还没提交完- 不能用来包需要立刻反映到受控输入上的那次
setText
useDeferredValue:推迟「派生值」
不想拆两份 state 时,可让某个值「慢一拍」参与重渲染:
const deferredText = useDeferredValue(text)
const list = useMemo(
() => items.filter((x) => x.includes(deferredText)),
[items, deferredText],
)
输入仍绑定 text(即时);贵的过滤依赖 deferredText。
transition vs deferredValue
useTransition | useDeferredValue | |
|---|---|---|
| 你控制什么 | 哪次 setState 可延迟 | 哪个 值 在渲染中可延迟 |
| 典型 | 事件里同时紧急 + 非紧急更新 | 已有一个高频变化的 value,下游重 |
两者可一起出现;面试能说清分工即可。
Suspense:等待「还没准备好的 UI」
Suspense 让组件在数据/代码未就绪时 抛出 Promise(或兼容机制),由边界展示 fallback。
<Suspense fallback={<Loading />}>
<UserProfile id={id} />
</Suspense>
常见搭配:
- 懒加载:
React.lazy(() => import('./Page')) - 数据:需与支持 Suspense 的数据库(如部分 RSC / relay / 实验性能力)配合;纯手写
useEffect拉数 不会 自 动进 Suspense
面试别吹过头:CSR 里「useEffect + loading 状态」仍是主流;Suspense 更像统一「等待中 UI」的边界模型,在 SSR/RSC 链路里更常见。
startTransition 误区
// ❌ 以为这样整页都不卡 —— 若更新本身在 commit 后仍极重,还是要虚拟列表等
startTransition(() => setHugeFilter(v))
// ❌ 把受控 input 的 value 更新放进 transition —— 输入会「跟手迟钝」
并发特性 降低的是调度与打断带来的卡顿,替代不了算法与虚拟列表。
和旧 API 的关系
useMemo/memo:减工作量- Concurrent:工作量大时 先保交互
- 两者互补,不是互相替代
面试速答清单
- Concurrent Mode 是多线程吗? → 不是;主线程可中断渲染 + 优先级。
- useTransition 解决什么? → 紧急更新(输入)优先于可延迟更新(过滤结果)。
- isPending 是什么? → 过渡更新尚未完成时的标记。
- Suspense 和 useEffect loading 区别? → 声明式边界 vs 组件内手写状态;数据源要支持。
- 何时用 deferredValue? → 已有快速变化的值,希望下游重渲染推迟。
上一篇:Fiber 与调度直觉 · 下一篇:高频场景题