跳到主要内容

useTransition和useDeferredValue

React.useTransitionuseDeferredValue 都是 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(在浏览器空闲时)。


🆚 核心差异对比表

特性useTransitionuseDeferredValue
谁发起更新开发者主动调用 startTransitionReact 自动管理
控制粒度整个状态更新过程单个值的渲染
是否需要 isPending✅ 是(用于 loading)❌ 否(无 pending 状态)
适用操作按钮点击、切换等离散操作输入框、滑块等连续输入
能否中断✅ 能(高优任务可打断)✅ 能
典型用途“执行一个耗时操作”“让某个值的变化更平滑”
代码位置在事件处理函数中在组件 render 逻辑中

🧪 场景选择指南

✅ 用 useTransition 当:

  • 用户点击“搜索”按钮后触发过滤
  • 切换标签页时加载新数据
  • 你需要明确知道更新何时开始,并可能显示 loading

✅ 用 useDeferredValue 当:

  • 用户在输入框打字,实时过滤列表(无需点击搜索)
  • 拖动滑块调整参数,实时预览效果
  • 你想自动防抖某个高频变化的值,且不需要 loading 提示

⚠️ 常见误区

❌ 误区 1:useDeferredValueuseTransition 的简化版

→ 不是!它们解决不同问题。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 的流畅体验