useTransition 和 startTransition 适合解决什么问题?
简化版
startTransition 用来把某些状态更新标记为非紧急更新,避免它们阻塞输入等紧急更新;useTransition 额外提供 isPending,便于展示过渡中的加载状态。它适合输入联动大列表、复杂 tab 切换等场景。
详细版
典型场景是输入框立即更新,搜索结果列表可以稍后更新。
const [isPending, startTransition] = useTransition()
function onChange(e) {
const next = e.target.value
setKeyword(next)
startTransition(() => {
setQuery(next)
})
}
setKeyword 是紧急更新,让输入框跟手;setQuery 触发大列表过滤,标为 transition 后可以被 React 延后或中断。isPending 可以显示“正在更新结果”。它不是防抖,也不会减少计算次数本身。
完整版教学
一、它解决的是紧急和非紧急更新抢主线程
用户输入时,输入框字符显示必须立刻发生。 但根据输入过滤大列表、渲染复杂图表、切换大面板,这些可以稍慢一点。 如果两类更新绑定在一起,同步渲染会让输入卡顿。
一次输入:
更新 input 值 1ms
过滤并渲染列表 80ms
用户感受: 输入卡了 80ms
transition 的思路是告诉 React:输入值很急,列表结果没那么急。 这样 React 可以优先处理输入,后处理列表。
二、startTransition 标记非紧急状态更新
startTransition 包住的状态更新会被标记为 transition。
这些更新可以被更紧急的更新打断。
它适合不需要立即完成但仍要最终展示的 UI 更新。
import { startTransition } from 'react'
function selectTab(tab) {
setActiveTab(tab)
startTransition(() => {
setHeavyPanel(tab)
})
}
| 更新 | 是否适合 transition | 原因 |
|---|---|---|
| 输入框受控值 | 不适合 | 必须立刻反馈 |
| 大列表过滤条件 | 适合 | 可以稍后更新 |
| 勾选按钮状态 | 通常不适合 | 用户期待立即响应 |
| 切换复杂内容区 | 适合 | 可显示 pending |
判断标准是用户是否会立刻感知到“没变”。 输入框不立刻变会很糟,搜索结果晚 100ms 通常可接受。
三、useTransition 比 startTransition 多一个 pending 状态
useTransition 返回 [isPending, startTransition]。
isPending 表示 transition 更新还没完成。
这可以用来展示轻量提示,避免用户以为页面没反应。
const [isPending, startTransition] = useTransition()
return (
<>
<input onChange={onChange} />
{isPending && <span>结果更新中...</span>}
<SearchList query={query} />
</>
)
如果列表更新需要 300ms,pending 提示可以帮助用户理解。 如果更新只有 20ms,提示可能闪烁,UI 上可以做延迟展示。
四、它不是防抖,也不是节流
防抖会减少触发次数,例如用户停下 300ms 后才请求。 transition 不一定减少计算次数,它只是降低更新优先级,让紧急渲染有机会先完成。 两者可以配合,但不能混为一谈。
防抖:
10 次输入 -> 1 次请求
transition:
10 次输入 -> 输入优先响应,结果更新可被中断或延后
如果你的瓶颈是频繁请求接口,防抖更直接。 如果瓶颈是本地大列表渲染影响输入,transition 更贴切。
五、transition 内部仍然要避免重计算浪费
标记低优先级不代表计算免费。 如果每次 transition 都同步过滤 100000 条数据,主线程仍然会忙。 工程里通常还要配合 memo、虚拟列表、索引、Web Worker 或分页。
const visible = useMemo(() => {
return items.filter(item => item.name.includes(query))
}, [items, query])
假设过滤 100000 条需要 50ms,列表渲染需要 70ms。 transition 能让输入先响应,但最终这 120ms 工作仍然存在。 优化总量和优化优先级是两回事。
六、异步边界和数据请求要分清
transition 标记的是 React 状态更新优先级。 它不会自动取消 fetch,也不会自动管理服务端数据缓存。 如果涉及请求,仍需要请求库、缓存、取消和错误处理。
记忆钩子:transition 不是少干活,是把“不急的 UI 更新”排到后面干。
面试中最好用搜索框例子。 先更新输入框,再 transition 更新搜索结果,顺手说明它不是 debounce。
七、常见误区与追问
- 误区:transition 可以让计算消失。 它调整更新优先级,不会凭空减少计算量。
- 误区:输入框 value 更新应该放进 startTransition。 受控输入值必须紧急更新,否则会卡手。
- 误区:useTransition 就是防抖。 防抖减少触发次数,transition 调整渲染优先级。
- 追问:isPending 有什么用? 用来展示非紧急更新尚未完成的提示或骨架状态。
- 追问:startTransition 和 useTransition 怎么选? 不需要 pending 状态时可用
startTransition,组件内要展示 pending 用useTransition。 - 追问:大列表还是卡怎么办? 配合虚拟列表、memo、数据索引、分页或 Worker,减少实际工作量。
八、加强记忆
这题围绕“急不急”回答。输入、点击反馈是急活;大列表、大面板更新是慢活。startTransition 给慢活降优先级,useTransition 还能告诉你慢活是否 pending。别把它说成防抖或性能银弹。