目录
概述
什么是 useTransition?
useTransition 是 React 18 引入的新 Hook,用于标记非紧急的状态更新。它允许组件在状态转换期间保持响应,通过将某些更新标记为"过渡"来推迟它们的渲染。
主要特点
- 保持 UI 响应性
- 区分紧急和非紧急更新
- 提供加载状态指示器
- 不会阻塞用户交互
基本用法
1. 基本语法
typescript
import { useTransition } from 'react';
function MyComponent() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
const handleClick = () => {
startTransition(() => {
setCount(c => c + 1); // 这个更新被标记为过渡,当某次更新造成页面阻塞时,用户点击其他组件操作,此时会降低此次更新的优先级,不阻塞页面渲染先更新优先级高的操作,startTransition中只能写同步的代码,异步代码会打断低优先级,比如不能使用setTimeout
});
};
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>Increment</button>
<p>Count: {count}</p>
</div>
);
}//isPending为true是表示当前渲染阻塞避免页面出现卡顿现象先显示loading状态组件,直到isPending为false时将展示渲染好的组件
2. 带加载状态的示例
typescript
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
// 立即更新输入值(紧急更新)
setQuery(e.target.value);
// 将搜索结果更新标记为过渡(非紧急更新)
startTransition(() => {
// 模拟搜索操作
const searchResults = performSearch(e.target.value);
setResults(searchResults);
});
};
return (
<div>
<input value={query} onChange={handleSearch} />
{isPending ? (
<div>Loading...</div>
) : (
<ul>
{results.map(result => (
<li key={result.id}>{result.title}</li>
))}
</ul>
)}
</div>
);
}
使用场景
1. 大量数据渲染
typescript
function DataGrid() {
const [data, setData] = useState([]);
const [isPending, startTransition] = useTransition();
const [filter, setFilter] = useState('');
const handleFilterChange = (newFilter: string) => {
setFilter(newFilter); // 立即更新过滤条件
startTransition(() => {
// 延迟大量数据的过滤和渲染
const filteredData = processLargeDataSet(newFilter);
setData(filteredData);
});
};
return (
<div>
<input
value={filter}
onChange={e => handleFilterChange(e.target.value)}
/>
{isPending ? (
<LoadingGrid />
) : (
<VirtualizedGrid data={data} />
)}
</div>
);
}
2. 路由切换
typescript
function App() {
const [isPending, startTransition] = useTransition();
const [currentPage, setCurrentPage] = useState('home');
const navigate = (page: string) => {
// 立即更新导航状态
startTransition(() => {
setCurrentPage(page);
});
};
return (
<div>
<Navigation onNavigate={navigate} />
{isPending ? (
<PageTransitionSpinner />
) : (
<Page name={currentPage} />
)}
</div>
);
}
3. 表单验证
typescript
function ComplexForm() {
const [formData, setFormData] = useState({});
const [errors, setErrors] = useState({});
const [isPending, startTransition] = useTransition();
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
// 立即更新表单值
setFormData(prev => ({
...prev,
[name]: value
}));
// 将复杂的验证逻辑标记为过渡
startTransition(() => {
const validationErrors = validateFormField(name, value);
setErrors(prev => ({
...prev,
[name]: validationErrors
}));
});
};
return (
<form>
<input
name="email"
onChange={handleChange}
value={formData.email || ''}
/>
{isPending ? (
<ValidatingIndicator />
) : (
errors.email && <ErrorMessage error={errors.email} />
)}
</form>
);
}
最佳实践
- 区分更新优先级
typescript
function UserProfile() {
const [isPending, startTransition] = useTransition();
const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
// 高优先级:直接更新输入值
setInputValue(e.target.value);
// 低优先级:更新预览
startTransition(() => {
setPreviewData(generatePreview(e.target.value));
});
};
}
- 合理使用 isPending
typescript
function LoadingStates() {
const [isPending, startTransition] = useTransition();
return (
<div>
{/* 使用骨架屏而不是简单的加载指示器 */}
{isPending ? (
<Skeleton />
) : (
<Content />
)}
</div>
);
}
注意事项
- 不要在 transition 中包含紧急更新
typescript
// ❌ 错误示例
startTransition(() => {
setInputValue(e.target.value); // 这应该是紧急更新
});
// ✅ 正确示例
setInputValue(e.target.value); // 紧急更新
startTransition(() => {
setSearchResults(search(e.target.value)); // 非紧急更新
});
- 避免不必要的 transition
typescript
// ❌ 不需要 transition
startTransition(() => {
setCount(count + 1); // 简单的状态更新不需要 transition
});
// ✅ 适合使用 transition
startTransition(() => {
setFilteredItems(items.filter(complexFilter)); // 复杂计算
});
总结
-
useTransition 适用场景:
- 大量数据处理
- 复杂 UI 更新
- 后台计算
- 非阻塞渲染
-
主要优势:
- 提升用户体验
- 保持 UI 响应性
- 优化渲染性能
- 提供加载状态
-
使用建议:
- 合理区分更新优先级
- 适当处理加载状态
- 避免过度使用
- 配合其他性能优化手段