React 中hooks之useDeferredValue用法总结
目录
- 概述
- 基本用法
- 与防抖节流的区别
- 使用场景
- 区分过时内容
- 最佳实践
概述
什么是 useDeferredValue?
useDeferredValue 是 React 18 引入的新 Hook,用于延迟更新某个不那么重要的部分。它接收一个值并返回该值的新副本,新副本会延迟更新。这种延迟是有益的,让紧急更新(如用户输入)优先于不紧急的更新(如渲染搜索结果列表)。
主要特点
- 自动处理延迟
- 与 Suspense 集成
- 不会像防抖和useTransition那样丢弃中间值
- 可以区分过时/最新内容
基本用法
1. 基本语法
import { useDeferredValue } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<SlowList query={deferredQuery} /> {/* 使用延迟值进行渲染 */}
</div>
);
}
2. 区分过时内容示例
function SearchResults() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery; // 判断内容是否过时
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<div style={{
opacity: isStale ? 0.8 : 1,
transition: 'opacity 0.2s ease'
}}>
<SlowList query={deferredQuery} />
</div>
{isStale && <div>Loading new results...</div>}
</div>
);
}
与防抖节流的区别
1. 防抖 (Debounce)
// 防抖示例
function SearchWithDebounce() {
const [query, setQuery] = useState('');
const debouncedSearch = useCallback(
debounce((value) => {
// 执行搜索
performSearch(value);
}, 500),
[]
);
// 会丢弃中间值,只处理最后一次输入
return (
<input
onChange={e => {
setQuery(e.target.value);
debouncedSearch(e.target.value);
}}
/>
);
}
2. 节流 (Throttle)
// 节流示例
function SearchWithThrottle() {
const [query, setQuery] = useState('');
const throttledSearch = useCallback(
throttle((value) => {
performSearch(value);
}, 100),
[]
);
// 固定时间间隔执行,可能会延迟响应
return (
<input
onChange={e => {
setQuery(e.target.value);
throttledSearch(e.target.value);
}}
/>
);
}
3. useDeferredValue
// useDeferredValue 示例
function SearchWithDeferred() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// React 会根据用户设备性能和当前 CPU 负载自动调整延迟
// 不会丢弃任何值,而是以较低优先级处理它们
return (
<>
<input onChange={e => setQuery(e.target.value)} />
<SlowList query={deferredQuery} />
</>
);
}
使用场景
1. 大列表渲染
function VirtualizedList({ items }) {
const [filter, setFilter] = useState('');
const deferredFilter = useDeferredValue(filter);
const isStale = filter !== deferredFilter;
const filteredItems = useMemo(
() => items.filter(item => item.includes(deferredFilter)),
[deferredFilter, items]
);
return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} />
<div style={{ opacity: isStale ? 0.8 : 1 }}>
{filteredItems.map(item => (
<ListItem key={item} item={item} />
))}
</div>
</div>
);
}
2. 实时预览
function MarkdownEditor() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
const isStale = text !== deferredText;
return (
<div className="editor">
<textarea
value={text}
onChange={e => setText(e.target.value)}
/>
<div className={`preview ${isStale ? 'stale' : ''}`}>
<MarkdownPreview text={deferredText} />
</div>
</div>
);
}
最佳实践
- 合理使用 useMemo
function SlowList({ text }) {
const deferredText = useDeferredValue(text);
const isStale = text !== deferredText;
// 使用 useMemo 避免不必要的重新计算
const items = useMemo(
() => computeExpensiveList(deferredText),
[deferredText]
);
return (
<div style={{ opacity: isStale ? 0.8 : 1 }}>
{items.map(item => (
<ListItem key={item.id} item={item} />
))}
</div>
);
}
- 优雅降级处理
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<div>
{isStale && (
<div className="stale-indicator">
Showing results for "{deferredQuery}"
<br />
Updating results for "{query}"...
</div>
)}
<ResultsList query={deferredQuery} />
</div>
);
}
总结
-
useDeferredValue vs 防抖/节流:
- useDeferredValue 不会丢弃更新
- 自动适应用户设备性能
- 与 React 并发特性集成
- 提供过时状态标识
-
适用场景:
- 大数据列表渲染
- 实时预览功能
- 复杂图表更新
- 搜索建议
-
最佳实践:
- 配合 useMemo 使用
- 提供加载状态反馈
- 合理处理过时内容
- 注意性能优化