目录
- 概述
- 基本用法
- 与防抖节流的区别
- 使用场景
- 区分过时内容
- 最佳实践
概述
什么是 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"><textareavalue={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 使用
- 提供加载状态反馈
- 合理处理过时内容
- 注意性能优化