React.useTransition
React.useTransition 是 React 18 引入的一个 并发特性(Concurrent Features) Hook,用于将非紧急的更新标记为“过渡(transition)”,从而让 React 能够优先处理高优先级任务(如用户输入、动画),避免界面卡顿,提升用户体验。
✅ 核心概念一句话总结:
useTransition让你可以把“不那么紧急”的状态更新(比如搜索、筛选、切换标签页)标记为低优先级,React 会先响应用户交互,再处理这些更新,保持 UI 流畅。
🔧 基本用法
import { useTransition, useState } from 'react';
function App() {
const [isPending, startTransition] = useTransition();
const [input, setInput] = useState('');
const [items, setItems] = useState(largeList);
const handleChange = (e) => {
const value = e.target.value;
setInput(value);
// 将“过滤列表”这个操作标记为 transition(低优先级)
startTransition(() => {
setItems(
largeList.filter(item =>
item.name.toLowerCase().includes(value.toLowerCase())
)
);
});
};
return (
<div>
<input value={input} onChange={handleChange} />
{/* 显示加载状态(可选) */}
{isPending && <div>正在筛选...</div>}
<List items={items} />
</div>
);
}
📌 useTransition 返回什么?
const [isPending, startTransition] = useTransition();
| 返回值 | 类型 | 说明 |
|---|---|---|
isPending | boolean | 表示是否有 transition 正在进行(可用于显示 loading) |
startTransition | function | 一个函数,接收一个回调,该回调内的状态更新会被标记为“过渡更新” |
⚡ 为什么需要 useTransition?
场景:在大型列表中搜索
- 用户在输入框打字(高优先级:必须即时响应)
- 每次输入都要过滤 10,000 条数据并重新渲染(低优先级:可以稍后处理)
❌ 不用 useTransition 的问题:
- 打字时界面卡顿(因为 React 被大计算阻塞)
- 输入延迟,体验差
✅ 用了 useTransition 后:
- 输入框立即响应(高优先级任务优先执行)
- 列表更新被“推迟”到浏览器空闲时再处理(低优先级)
- 即使列表还没更新完,用户仍可继续输入或点击其他按钮
💡 React 会中断低优先级的渲染,去处理新的高优先级事件(如新按键),实现“可中断渲染”。
🆚 对比:普通更新 vs Transition 更新
| 行为 | 普通 setState | startTransition(setState) |
|---|---|---|
| 优先级 | 高(立即渲染) | 低(可中断、可推迟) |
| 是否阻塞 UI | 是 | 否 |
| 触发 Suspense fallback | ❌ 不会 | ✅ 会(如果配合 Suspense) |
isPending 状态 | 无 | 有(可用于 loading 提示) |
🧪 实际效果演示
假设你有一个慢操作:
// 模拟耗时计算
const filterItems = (query) => {
const start = Date.now();
while (Date.now() - start < 2000) {} // 阻塞 2 秒!
return originalList.filter(...);
};
- 不用 transition:点击按钮后,整个页面冻结 2 秒。
- 用 transition:点击后 UI 依然可交互(比如还能点取消),2 秒后结果才出现。
⚠️ 注意事项
1. 只能用于状态更新
startTransition(() => {
setCount(c => c + 1); // ✅ OK
fetchData(); // ❌ 无效!异步操作不会被标记为 transition
});
startTransition只影响 同步的状态更新。如果你要结合数据请求,需配合Suspense+ 缓存(如 Relay、SWR)。
2. 不要用于紧急更新
以下情况不要用 useTransition:
- 表单提交
- 按钮点击反馈(如“已添加到购物车”)
- 任何需要立即视觉反馈的操作
3. 与 useDeferredValue 的区别
| Hook | 用途 |
|---|---|
useTransition | 主动触发一个低优先级更新(你控制何时开始) |
useDeferredValue | 被动延迟某个值的更新(常用于输入防抖场景) |
// useDeferredValue 示例
const deferredQuery = useDeferredValue(inputValue);
// 当 inputValue 变化快时,deferredQuery 会滞后,避免频繁重渲染
✅ 最佳实践
- 用于搜索、筛选、标签切换等“非即时”操作
- 配合
isPending显示微弱 loading(不要用全屏遮罩!){isPending && <Spinner size="small" />} - 不要包裹异步逻辑(如 API 调用),它只影响 React 的渲染调度
🌐 浏览器兼容性
- 需要 React 18+
- 依赖 React 的并发渲染机制(自动启用,无需额外配置)
✅ 总结
| 关键点 | 说明 |
|---|---|
| 目的 | 避免非关键更新阻塞 UI,提升响应性 |
| 核心 API | const [isPending, startTransition] = useTransition() |
| 使用方式 | 把 setState 包在 startTransition(() => { ... }) 中 |
| 适用场景 | 搜索、筛选、复杂列表更新等 |
| 不适用场景 | 表单提交、即时反馈、异步请求 |
💡 记住:
“用户输入是皇帝,其他都是臣民。”
用useTransition给“臣民”排队,让“皇帝”优先通行!
这是构建流畅、专业级 React 应用的重要工具之一。