useTransition和useDeferredValue
React.useTransition 和 useDeferredValue 都是 React 18 引入的 并发特性(Concurrent Features),用于优化 UI 响应性,避免因高开销更新导致界面卡顿。
但它们的使用场景、控制方式和作用对象有本质区别。
✅ 一句话总结区别
| Hook | 控制方式 | 作用对象 | 典型场景 |
|---|---|---|---|
useTransition | 主动触发一个低优先级更新 | 状态更新逻辑本身 | “点击按钮后延迟更新列表” |
useDeferredValue | 被动延迟某个值的渲染 | 某个具体 state 的“延迟副本” | “输入框打字时防抖式过滤列表” |
🔍 详细对比
1. useTransition:主动标记“这个更新不紧急”
- 你显式调用
startTransition来包裹状态更新。 - 适用于由用户操作触发的非紧急更新(如搜索、筛选、切换标签)。
- 你可以获得
isPending状态用于显示 loading。
📌 示例:点击按钮后过渡更新
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('all');
const handleTabChange = (newTab) => {
// 立即更新 tab(高优先级)
setTab(newTab);
// 将耗时的列表过滤标记为低优先级
startTransition(() => {
setFilteredItems(expensiveFilter(newTab));
});
};
return (
<div>
<button onClick={() => handleTabChange('active')}>Active</button>
{isPending && <Spinner />} {/* 可显示 loading */}
<ItemList items={filteredItems} />
</div>
);
✅ 你控制何时开始 transition。
2. useDeferredValue:自动延迟“这个值的渲染”
- 你传入一个 当前值(如 input 输入),它返回一个 延迟版本的值。
- 当原始值变化很快时,延迟值会“滞后”,避免频繁重渲染。
- 无需手动触发,React 自动管理延迟。
📌 示例:输入框实时搜索(防抖效果)
const [inputValue, setInputValue] = useState('');
// 创建一个“延迟版本”的 inputValue
const deferredValue = useDeferredValue(inputValue);
// 列表只在 deferredValue 稳定时才更新
const filteredList = useMemo(() =>
list.filter(item => item.includes(deferredValue)),
[deferredValue] // 注意:依赖的是 deferredValue!
);
return (
<div>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)} // 立即响应输入
/>
{/* 即使 inputValue 频繁变,filteredList 不会立即重算 */}
<ItemList items={filteredList} />
</div>
);
✅ React 自动决定何时更新 deferredValue(在浏览器空闲时)。
🆚 核心差异对比表
| 特性 | useTransition | useDeferredValue |
|---|---|---|
| 谁发起更新 | 开发者主动调用 startTransition | React 自动管理 |
| 控制粒度 | 整个状态更新过程 | 单个值的渲染 |
是否需要 isPending | ✅ 是(用于 loading) | ❌ 否(无 pending 状态) |
| 适用操作 | 按钮点击、切换等离散操作 | 输入框、滑块等连续输入 |
| 能否中断 | ✅ 能(高优任务可打断) | ✅ 能 |
| 典型用途 | “执行一个耗时操作” | “让某个值的变化更平滑” |
| 代码位置 | 在事件处理函数中 | 在组件 render 逻辑中 |
🧪 场景选择指南
✅ 用 useTransition 当:
- 用户点击“搜索”按钮后触发过滤
- 切换标签页时加载新数据
- 你需要明确知道更新何时开始,并可能显示 loading
✅ 用 useDeferredValue 当:
- 用户在输入框打字,实时过滤列表(无需点击搜索)
- 拖动滑块调整参数,实时预览效果
- 你想自动防抖某个高频变化的值,且不需要 loading 提示
⚠️ 常见误区
❌ 误区 1:useDeferredValue 是 useTransition 的简化版
→ 不是!它们解决不同问题。useDeferredValue 本质是创建一个“延迟派生状态”。
❌ 误区 2:可以用 useDeferredValue 替代所有 useTransition
→ 不行!如果你需要在更新前做异步操作(如 API 调用),必须用 useTransition + Suspense。
✅ 正确组合使用(高级):
// 先用 useDeferredValue 获取稳定查询词
const deferredQuery = useDeferredValue(query);
// 再用 useTransition 触发实际搜索(如果涉及 Suspense)
useEffect(() => {
if (deferredQuery) {
startTransition(() => {
setSearchResult(api.search(deferredQuery)); // 可能抛出 Promise(触发 Suspense)
});
}
}, [deferredQuery]);
✅ 总结记忆口诀
useTransition→ “我来安排”:你主动说:“接下来这个更新不急,先干别的。”useDeferredValue→ “你慢慢来”:你告诉 React:“这个值变太快,你找个空闲再用它。”
两者都是为了 让 React 优先响应用户交互,但一个是你发号施令,一个是让 React自主调度。
合理使用它们,你的应用将拥有 60fps 的流畅体验!