Skip to main content

并发特性

建立在 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

useTransitionuseDeferredValue
你控制什么哪次 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:工作量大时 先保交互
  • 两者互补,不是互相替代

面试速答清单

  1. Concurrent Mode 是多线程吗? → 不是;主线程可中断渲染 + 优先级。
  2. useTransition 解决什么? → 紧急更新(输入)优先于可延迟更新(过滤结果)。
  3. isPending 是什么? → 过渡更新尚未完成时的标记。
  4. Suspense 和 useEffect loading 区别? → 声明式边界 vs 组件内手写状态;数据源要支持。
  5. 何时用 deferredValue? → 已有快速变化的值,希望下游重渲染推迟。

上一篇:Fiber 与调度直觉 · 下一篇:高频场景题