Appearance
useDeferredValue延迟值
第一部分:useDeferredValue基础
1.1 什么是useDeferredValue
useDeferredValue是React 18引入的Hook,用于延迟更新非关键的UI部分。它接收一个值并返回该值的"延迟"版本,这个延迟版本在紧急更新完成后才会更新。
基本语法:
javascript
import { useDeferredValue } from 'react';
function Component() {
const [input, setInput] = useState('');
const deferredInput = useDeferredValue(input);
// input: 立即更新的值
// deferredInput: 延迟更新的值
return (
<div>
<input value={input} onChange={e => setInput(e.target.value)} />
<ExpensiveComponent value={deferredInput} />
</div>
);
}1.2 工作原理
javascript
// useDeferredValue的内部机制(简化)
function useDeferredValue(value) {
const [deferredValue, setDeferredValue] = useState(value);
useEffect(() => {
// 使用低优先级更新延迟值
startTransition(() => {
setDeferredValue(value);
});
}, [value]);
return deferredValue;
}
// 实际执行流程
// 1. input改变 -> 立即更新
// 2. deferredInput保持旧值 -> UI先响应用户输入
// 3. 在空闲时 -> 更新deferredInput -> 重新渲染ExpensiveComponent1.3 为什么需要useDeferredValue
问题场景:
javascript
// 问题:耗时渲染阻塞用户输入
function SearchPage() {
const [query, setQuery] = useState('');
const handleInput = (e) => {
setQuery(e.target.value);
};
return (
<div>
<input value={query} onChange={handleInput} />
{/* 耗时组件直接使用query,导致输入卡顿 */}
<ExpensiveSearchResults query={query} />
</div>
);
}
// ExpensiveSearchResults渲染耗时500ms
// 用户输入时,每次按键都要等待500ms
// 结果:输入框严重卡顿useDeferredValue的解决方案:
javascript
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const handleInput = (e) => {
setQuery(e.target.value);
};
return (
<div>
{/* input使用原始值,立即响应 */}
<input value={query} onChange={handleInput} />
{/* 耗时组件使用延迟值 */}
<ExpensiveSearchResults query={deferredQuery} />
</div>
);
}
// 执行流程:
// 1. 用户输入 'a' -> query立即变为'a' -> input立即显示'a'
// 2. deferredQuery还是'' -> ExpensiveSearchResults暂不更新
// 3. 用户继续输入 'ab' -> query变'ab' -> input显示'ab'
// 4. 等输入停止 -> deferredQuery更新为'ab' -> ExpensiveSearchResults渲染1.4 基本使用
javascript
// 1. 基本延迟
function BasicDefer() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<p>当前输入: {text}</p>
<p>延迟显示: {deferredText}</p>
<HeavyComponent value={deferredText} />
</div>
);
}
// 2. 数字值延迟
function NumberDefer() {
const [count, setCount] = useState(0);
const deferredCount = useDeferredValue(count);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
<ExpensiveChart value={deferredCount} />
</div>
);
}
// 3. 对象值延迟
function ObjectDefer() {
const [filters, setFilters] = useState({ category: 'all', price: 100 });
const deferredFilters = useDeferredValue(filters);
return (
<div>
<FilterControls
filters={filters}
onChange={setFilters}
/>
<ProductList filters={deferredFilters} />
</div>
);
}
// 4. 数组值延迟
function ArrayDefer() {
const [items, setItems] = useState([]);
const deferredItems = useDeferredValue(items);
const addItem = () => {
setItems(prev => [...prev, { id: Date.now(), name: 'New Item' }]);
};
return (
<div>
<button onClick={addItem}>Add Item ({items.length})</button>
<LargeList items={deferredItems} />
</div>
);
}第二部分:防抖效果对比
2.1 传统防抖
javascript
// 传统debounce实现
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
// 使用传统防抖
function TraditionalDebounce() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
// 防抖搜索函数
const debouncedSearch = useMemo(
() => debounce((value) => {
setResults(performSearch(value));
}, 300),
[]
);
const handleInput = (e) => {
const value = e.target.value;
setQuery(value);
debouncedSearch(value);
};
return (
<div>
<input value={query} onChange={handleInput} />
<SearchResults results={results} />
</div>
);
}
// 传统防抖的问题:
// 1. 固定延迟时间
// 2. 可能在用户停止输入前不显示任何结果
// 3. 不能被更高优先级的更新打断2.2 useDeferredValue方式
javascript
// 使用useDeferredValue
function DeferredValueApproach() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const results = useMemo(
() => performSearch(deferredQuery),
[deferredQuery]
);
return (
<div>
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
<SearchResults results={results} />
</div>
);
}
// useDeferredValue的优势:
// 1. 智能调度,无固定延迟
// 2. 立即显示旧结果,流畅更新到新结果
// 3. 可以被打断
// 4. 与React调度器深度集成2.3 详细对比
javascript
// 完整对比示例
function DebounceComparison() {
const [method, setMethod] = useState('deferred');
const [input, setInput] = useState('');
// 方法1:传统防抖
const [debouncedInput, setDebouncedInput] = useState('');
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedInput(input);
}, 300);
return () => clearTimeout(timer);
}, [input]);
// 方法2:useDeferredValue
const deferredInput = useDeferredValue(input);
const displayValue = method === 'debounce' ? debouncedInput : deferredInput;
return (
<div>
<div>
<label>
<input
type="radio"
value="debounce"
checked={method === 'debounce'}
onChange={e => setMethod(e.target.value)}
/>
传统防抖
</label>
<label>
<input
type="radio"
value="deferred"
checked={method === 'deferred'}
onChange={e => setMethod(e.target.value)}
/>
useDeferredValue
</label>
</div>
<input
value={input}
onChange={e => setInput(e.target.value)}
placeholder="输入搜索..."
/>
<div className="comparison">
<div>
<h3>原始值(立即)</h3>
<p>{input}</p>
</div>
<div>
<h3>延迟值</h3>
<p>{displayValue}</p>
</div>
</div>
<HeavyComponent value={displayValue} />
</div>
);
}2.4 性能对比
javascript
// 性能测试
function PerformanceComparison() {
const [input, setInput] = useState('');
const [metrics, setMetrics] = useState({
debounce: [],
deferred: []
});
// 传统防抖
const [debouncedInput, setDebouncedInput] = useState('');
useEffect(() => {
const start = performance.now();
const timer = setTimeout(() => {
setDebouncedInput(input);
const duration = performance.now() - start;
setMetrics(m => ({
...m,
debounce: [...m.debounce, duration].slice(-10)
}));
}, 300);
return () => clearTimeout(timer);
}, [input]);
// useDeferredValue
const deferredInput = useDeferredValue(input);
useEffect(() => {
const start = performance.now();
const duration = performance.now() - start;
setMetrics(m => ({
...m,
deferred: [...m.deferred, duration].slice(-10)
}));
}, [deferredInput]);
const avgDebounce = metrics.debounce.length > 0
? metrics.debounce.reduce((a, b) => a + b, 0) / metrics.debounce.length
: 0;
const avgDeferred = metrics.deferred.length > 0
? metrics.deferred.reduce((a, b) => a + b, 0) / metrics.deferred.length
: 0;
return (
<div>
<input value={input} onChange={e => setInput(e.target.value)} />
<div className="metrics">
<div>
<h3>传统防抖</h3>
<p>平均延迟: {avgDebounce.toFixed(2)}ms</p>
<p>固定等待: 300ms</p>
</div>
<div>
<h3>useDeferredValue</h3>
<p>平均延迟: {avgDeferred.toFixed(2)}ms</p>
<p>智能调度</p>
</div>
</div>
</div>
);
}2.5 何时使用哪种方式
javascript
// 选择指南
const chooseApproach = (scenario) => {
const guidelines = {
// 使用传统防抖的场景
useDebounce: [
'需要精确控制延迟时间',
'API请求限流',
'输入验证(如用户名检查)',
'自动保存功能',
'窗口resize/scroll事件'
],
// 使用useDeferredValue的场景
useDeferredValue: [
'优化UI渲染性能',
'搜索结果显示',
'实时过滤/排序',
'数据可视化更新',
'复杂列表渲染'
]
};
return guidelines;
};
// 场景1:API请求 -> 使用防抖
function APIRequest() {
const [query, setQuery] = useState('');
const debouncedFetch = useMemo(
() => debounce((q) => {
fetch(`/api/search?q=${q}`);
}, 500),
[]
);
const handleInput = (e) => {
const value = e.target.value;
setQuery(value);
debouncedFetch(value); // 防抖请求
};
}
// 场景2:UI渲染 -> 使用useDeferredValue
function UIRendering() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const results = useMemo(
() => filterLocalData(deferredQuery),
[deferredQuery]
);
return <ResultsList results={results} />;
}
// 场景3:混合使用
function HybridApproach() {
const [input, setInput] = useState('');
const deferredInput = useDeferredValue(input); // UI延迟
// API请求防抖
const debouncedAPI = useMemo(
() => debounce((value) => {
fetch(`/api/search?q=${value}`);
}, 500),
[]
);
useEffect(() => {
if (deferredInput) {
debouncedAPI(deferredInput);
}
}, [deferredInput, debouncedAPI]);
return (
<div>
<input value={input} onChange={e => setInput(e.target.value)} />
<LocalResults query={deferredInput} />
</div>
);
}第三部分:实战案例
3.1 搜索功能
javascript
// 基础搜索
function SearchComponent() {
const [searchText, setSearchText] = useState('');
const deferredSearchText = useDeferredValue(searchText);
const searchResults = useMemo(() => {
if (!deferredSearchText) return [];
return largeDataset.filter(item =>
item.title.toLowerCase().includes(deferredSearchText.toLowerCase()) ||
item.description.toLowerCase().includes(deferredSearchText.toLowerCase())
);
}, [deferredSearchText]);
const isStale = searchText !== deferredSearchText;
return (
<div className="search-container">
<input
type="text"
value={searchText}
onChange={e => setSearchText(e.target.value)}
placeholder="搜索..."
className="search-input"
/>
<div className={isStale ? 'results-stale' : 'results'}>
{searchResults.map(item => (
<SearchResultItem key={item.id} item={item} />
))}
</div>
</div>
);
}
// 高级搜索:带高亮
function AdvancedSearch() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const results = useMemo(() => {
return searchWithHighlight(data, deferredQuery);
}, [deferredQuery]);
const isSearching = query !== deferredQuery;
return (
<div>
<div className="search-bar">
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
{isSearching && <SearchingIndicator />}
</div>
<div className="results">
{results.map(result => (
<div key={result.id}>
<h3 dangerouslySetInnerHTML={{ __html: result.titleHTML }} />
<p dangerouslySetInnerHTML={{ __html: result.descHTML }} />
</div>
))}
</div>
</div>
);
}
// 搜索工具函数
function searchWithHighlight(data, query) {
if (!query) return data.map(item => ({
...item,
titleHTML: item.title,
descHTML: item.description
}));
const regex = new RegExp(`(${escapeRegex(query)})`, 'gi');
return data
.filter(item =>
item.title.toLowerCase().includes(query.toLowerCase()) ||
item.description.toLowerCase().includes(query.toLowerCase())
)
.map(item => ({
...item,
titleHTML: item.title.replace(regex, '<mark>$1</mark>'),
descHTML: item.description.replace(regex, '<mark>$1</mark>')
}));
}
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}3.2 数据过滤
javascript
// 复杂过滤器
function DataFilter() {
const [filters, setFilters] = useState({
search: '',
category: 'all',
priceRange: [0, 1000],
inStock: false
});
const deferredFilters = useDeferredValue(filters);
const filteredData = useMemo(() => {
let result = allProducts;
if (deferredFilters.search) {
result = result.filter(p =>
p.name.toLowerCase().includes(deferredFilters.search.toLowerCase())
);
}
if (deferredFilters.category !== 'all') {
result = result.filter(p => p.category === deferredFilters.category);
}
result = result.filter(p =>
p.price >= deferredFilters.priceRange[0] &&
p.price <= deferredFilters.priceRange[1]
);
if (deferredFilters.inStock) {
result = result.filter(p => p.stock > 0);
}
return result;
}, [deferredFilters]);
const isFiltering = filters !== deferredFilters;
return (
<div className="filter-page">
<aside className="filters">
<input
type="text"
value={filters.search}
onChange={e => setFilters(f => ({ ...f, search: e.target.value }))}
placeholder="搜索产品..."
/>
<select
value={filters.category}
onChange={e => setFilters(f => ({ ...f, category: e.target.value }))}
>
<option value="all">所有分类</option>
<option value="electronics">电子产品</option>
<option value="clothing">服装</option>
<option value="books">图书</option>
</select>
<PriceRangeSlider
value={filters.priceRange}
onChange={range => setFilters(f => ({ ...f, priceRange: range }))}
/>
<label>
<input
type="checkbox"
checked={filters.inStock}
onChange={e => setFilters(f => ({ ...f, inStock: e.target.checked }))}
/>
仅显示有货
</label>
</aside>
<main className="products">
{isFiltering && <FilteringOverlay />}
<div className="product-count">
共 {filteredData.length} 个产品
</div>
<ProductGrid products={filteredData} />
</main>
</div>
);
}3.3 实时图表
javascript
// 实时更新的图表
function LiveChart() {
const [dataPoints, setDataPoints] = useState([]);
const [updateInterval, setUpdateInterval] = useState(100);
const deferredDataPoints = useDeferredValue(dataPoints);
useEffect(() => {
const interval = setInterval(() => {
setDataPoints(prev => [
...prev.slice(-99),
{
time: Date.now(),
value: Math.random() * 100
}
]);
}, updateInterval);
return () => clearInterval(interval);
}, [updateInterval]);
const isUpdating = dataPoints !== deferredDataPoints;
return (
<div>
<div className="controls">
<label>
更新频率: {updateInterval}ms
<input
type="range"
min="50"
max="1000"
step="50"
value={updateInterval}
onChange={e => setUpdateInterval(Number(e.target.value))}
/>
</label>
{isUpdating && <span className="updating">更新中...</span>}
</div>
<LineChart data={deferredDataPoints} />
</div>
);
}
// 交互式数据可视化
function InteractiveVisualization() {
const [selectedMetric, setSelectedMetric] = useState('revenue');
const [dateRange, setDateRange] = useState({ start: '2024-01', end: '2024-12' });
const deferredMetric = useDeferredValue(selectedMetric);
const deferredDateRange = useDeferredValue(dateRange);
const chartData = useMemo(() => {
return computeChartData(deferredMetric, deferredDateRange);
}, [deferredMetric, deferredDateRange]);
const isComputing =
selectedMetric !== deferredMetric ||
dateRange !== deferredDateRange;
return (
<div>
<div className="viz-controls">
<select
value={selectedMetric}
onChange={e => setSelectedMetric(e.target.value)}
>
<option value="revenue">收入</option>
<option value="users">用户数</option>
<option value="engagement">参与度</option>
</select>
<DateRangePicker
value={dateRange}
onChange={setDateRange}
/>
</div>
{isComputing && <ComputingIndicator />}
<Chart data={chartData} />
</div>
);
}3.4 大列表优化
javascript
// 大列表渲染优化
function LargeList() {
const [sortBy, setSortBy] = useState('name');
const [filterText, setFilterText] = useState('');
const deferredSortBy = useDeferredValue(sortBy);
const deferredFilterText = useDeferredValue(filterText);
const processedItems = useMemo(() => {
let items = [...largeDataset];
// 过滤
if (deferredFilterText) {
items = items.filter(item =>
item.name.toLowerCase().includes(deferredFilterText.toLowerCase())
);
}
// 排序
items.sort((a, b) => {
if (deferredSortBy === 'name') {
return a.name.localeCompare(b.name);
} else if (deferredSortBy === 'date') {
return new Date(b.date) - new Date(a.date);
} else if (deferredSortBy === 'price') {
return b.price - a.price;
}
return 0;
});
return items;
}, [deferredSortBy, deferredFilterText]);
const isProcessing =
sortBy !== deferredSortBy ||
filterText !== deferredFilterText;
return (
<div className="list-container">
<div className="list-controls">
<input
type="text"
value={filterText}
onChange={e => setFilterText(e.target.value)}
placeholder="过滤..."
/>
<select value={sortBy} onChange={e => setSortBy(e.target.value)}>
<option value="name">按名称</option>
<option value="date">按日期</option>
<option value="price">按价格</option>
</select>
</div>
{isProcessing && <ProcessingBadge />}
<div className={isProcessing ? 'list-updating' : 'list'}>
{processedItems.map(item => (
<ListItem key={item.id} item={item} />
))}
</div>
</div>
);
}
// 虚拟滚动 + useDeferredValue
function VirtualizedList() {
const [filterQuery, setFilterQuery] = useState('');
const deferredQuery = useDeferredValue(filterQuery);
const filteredItems = useMemo(() => {
if (!deferredQuery) return allItems;
return allItems.filter(item =>
item.content.toLowerCase().includes(deferredQuery.toLowerCase())
);
}, [deferredQuery]);
return (
<div>
<input
value={filterQuery}
onChange={e => setFilterQuery(e.target.value)}
/>
<VirtualList
items={filteredItems}
height={600}
itemHeight={50}
renderItem={(item) => <ListItem item={item} />}
/>
</div>
);
}3.5 Tab切换
javascript
// Tab内容延迟加载
function TabsWithDeferred() {
const [activeTab, setActiveTab] = useState('overview');
const deferredActiveTab = useDeferredValue(activeTab);
const tabContent = useMemo(() => {
switch (deferredActiveTab) {
case 'overview':
return <OverviewContent />;
case 'analytics':
return <AnalyticsContent />;
case 'settings':
return <SettingsContent />;
default:
return null;
}
}, [deferredActiveTab]);
const isLoading = activeTab !== deferredActiveTab;
return (
<div className="tabs">
<div className="tab-buttons">
<button
className={activeTab === 'overview' ? 'active' : ''}
onClick={() => setActiveTab('overview')}
>
概览
</button>
<button
className={activeTab === 'analytics' ? 'active' : ''}
onClick={() => setActiveTab('analytics')}
>
分析
</button>
<button
className={activeTab === 'settings' ? 'active' : ''}
onClick={() => setActiveTab('settings')}
>
设置
</button>
</div>
{isLoading && <TabLoadingBar />}
<div className="tab-content">
{tabContent}
</div>
</div>
);
}3.6 表单自动保存
javascript
// 表单自动保存优化
function AutoSaveForm() {
const [formData, setFormData] = useState({
title: '',
content: '',
tags: []
});
const deferredFormData = useDeferredValue(formData);
// 延迟保存
useEffect(() => {
const saveTimer = setTimeout(() => {
saveToLocalStorage(deferredFormData);
}, 500);
return () => clearTimeout(saveTimer);
}, [deferredFormData]);
const isSaving = formData !== deferredFormData;
return (
<form className="auto-save-form">
<div className="save-status">
{isSaving ? '保存中...' : '已保存'}
</div>
<input
type="text"
value={formData.title}
onChange={e => setFormData(f => ({ ...f, title: e.target.value }))}
placeholder="标题"
/>
<textarea
value={formData.content}
onChange={e => setFormData(f => ({ ...f, content: e.target.value }))}
placeholder="内容"
rows={10}
/>
<TagInput
value={formData.tags}
onChange={tags => setFormData(f => ({ ...f, tags }))}
/>
</form>
);
}第四部分:高级技巧
4.1 配合Suspense使用
javascript
// useDeferredValue + Suspense
function DeferredWithSuspense() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<div>
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
<Suspense fallback={<SearchSkeleton />}>
<SearchResults query={deferredQuery} />
</Suspense>
</div>
);
}
// SearchResults组件
function SearchResults({ query }) {
// 使用Suspense数据获取
const results = use(fetchSearchResults(query));
return (
<div className="results">
{results.map(result => (
<ResultCard key={result.id} data={result} />
))}
</div>
);
}4.2 多个延迟值
javascript
// 管理多个延迟值
function MultipleDeferred() {
const [search, setSearch] = useState('');
const [category, setCategory] = useState('all');
const [priceRange, setPriceRange] = useState([0, 1000]);
const deferredSearch = useDeferredValue(search);
const deferredCategory = useDeferredValue(category);
const deferredPriceRange = useDeferredValue(priceRange);
const results = useMemo(() => {
return filterProducts({
search: deferredSearch,
category: deferredCategory,
priceRange: deferredPriceRange
});
}, [deferredSearch, deferredCategory, deferredPriceRange]);
const isAnyStale =
search !== deferredSearch ||
category !== deferredCategory ||
priceRange !== deferredPriceRange;
return (
<div>
<input value={search} onChange={e => setSearch(e.target.value)} />
<CategorySelect value={category} onChange={setCategory} />
<PriceSlider value={priceRange} onChange={setPriceRange} />
{isAnyStale && <Updating />}
<Results data={results} />
</div>
);
}4.3 条件延迟
javascript
// 条件性使用延迟值
function ConditionalDeferred() {
const [value, setValue] = useState('');
const [shouldDefer, setShouldDefer] = useState(true);
// 条件性延迟
const deferredValue = useDeferredValue(value);
const displayValue = shouldDefer ? deferredValue : value;
return (
<div>
<label>
<input
type="checkbox"
checked={shouldDefer}
onChange={e => setShouldDefer(e.target.checked)}
/>
启用延迟渲染
</label>
<input value={value} onChange={e => setValue(e.target.value)} />
<HeavyComponent value={displayValue} />
</div>
);
}
// 根据数据量决定是否延迟
function SmartDeferred() {
const [items, setItems] = useState([]);
// 只有数据量大时才延迟
const shouldDefer = items.length > 1000;
const deferredItems = useDeferredValue(items);
const displayItems = shouldDefer ? deferredItems : items;
return (
<div>
<AddItemButton onClick={newItem => setItems(i => [...i, newItem])} />
{shouldDefer && items !== deferredItems && <Updating />}
<ItemList items={displayItems} />
</div>
);
}4.4 性能监控
javascript
// 监控延迟性能
function MonitoredDeferred() {
const [value, setValue] = useState('');
const deferredValue = useDeferredValue(value);
const [metrics, setMetrics] = useState([]);
useEffect(() => {
if (value !== deferredValue) {
const startTime = performance.now();
return () => {
const duration = performance.now() - startTime;
setMetrics(m => [...m.slice(-19), duration]);
if (duration > 100) {
console.warn('Slow deferred update:', duration);
}
};
}
}, [value, deferredValue]);
const avgDelay = metrics.length > 0
? metrics.reduce((a, b) => a + b, 0) / metrics.length
: 0;
return (
<div>
<div>平均延迟: {avgDelay.toFixed(2)}ms</div>
<input value={value} onChange={e => setValue(e.target.value)} />
<HeavyComponent value={deferredValue} />
</div>
);
}第五部分:常见模式
5.1 搜索建议
javascript
// 搜索建议(不卡顿)
function SearchSuggestions() {
const [input, setInput] = useState('');
const deferredInput = useDeferredValue(input);
const suggestions = useMemo(() => {
if (!deferredInput) return [];
return allSuggestions
.filter(s => s.toLowerCase().startsWith(deferredInput.toLowerCase()))
.slice(0, 10);
}, [deferredInput]);
return (
<div className="search-box">
<input
value={input}
onChange={e => setInput(e.target.value)}
placeholder="搜索..."
/>
{suggestions.length > 0 && (
<ul className="suggestions">
{suggestions.map(suggestion => (
<li key={suggestion} onClick={() => setInput(suggestion)}>
{suggestion}
</li>
))}
</ul>
)}
</div>
);
}5.2 实时预览
javascript
// Markdown实时预览
function MarkdownEditor() {
const [markdown, setMarkdown] = useState('');
const deferredMarkdown = useDeferredValue(markdown);
const html = useMemo(() => {
return renderMarkdown(deferredMarkdown);
}, [deferredMarkdown]);
const isRendering = markdown !== deferredMarkdown;
return (
<div className="editor-container">
<div className="editor-pane">
<textarea
value={markdown}
onChange={e => setMarkdown(e.target.value)}
placeholder="输入Markdown..."
/>
</div>
<div className="preview-pane">
{isRendering && <RenderingIndicator />}
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>
</div>
);
}5.3 拖拽排序
javascript
// 拖拽排序优化
function DraggableSortList() {
const [items, setItems] = useState(initialItems);
const deferredItems = useDeferredValue(items);
const handleDragEnd = (result) => {
if (!result.destination) return;
const newItems = Array.from(items);
const [removed] = newItems.splice(result.source.index, 1);
newItems.splice(result.destination.index, 0, removed);
setItems(newItems);
};
return (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="list">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{deferredItems.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<ListItem item={item} />
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}注意事项
1. 使用限制
javascript
// ❌ 不要延迟关键交互
function Bad() {
const [isOpen, setIsOpen] = useState(false);
const deferredIsOpen = useDeferredValue(isOpen); // 错误!
return (
<Modal isOpen={deferredIsOpen}> {/* 模态框会延迟打开 */}
Content
</Modal>
);
}
// ✅ 只延迟非关键部分
function Good() {
const [isOpen, setIsOpen] = useState(false);
const [content, setContent] = useState('');
const deferredContent = useDeferredValue(content);
return (
<Modal isOpen={isOpen}> {/* 立即打开 */}
<HeavyContent data={deferredContent} /> {/* 内容延迟 */}
</Modal>
);
}2. 值比较
javascript
// 对象引用问题
function ReferenceIssue() {
const [filters, setFilters] = useState({ search: '' });
const deferredFilters = useDeferredValue(filters);
// 问题:每次都是新对象,总是不相等
useEffect(() => {
console.log('Filters changed');
}, [deferredFilters]); // 频繁触发
}
// 解决方案1:使用基本类型
function Solution1() {
const [search, setSearch] = useState('');
const deferredSearch = useDeferredValue(search);
useEffect(() => {
console.log('Search changed');
}, [deferredSearch]); // 正确比较
}
// 解决方案2:使用useMemo稳定引用
function Solution2() {
const [search, setSearch] = useState('');
const [category, setCategory] = useState('all');
const filters = useMemo(
() => ({ search, category }),
[search, category]
);
const deferredFilters = useDeferredValue(filters);
}3. 性能考虑
javascript
// 避免过度延迟
function OverDeferred() {
const [a, setA] = useState('');
const [b, setB] = useState('');
const [c, setC] = useState('');
const deferredA = useDeferredValue(a);
const deferredB = useDeferredValue(b);
const deferredC = useDeferredValue(c);
// 问题:三层延迟,过度复杂
const result = useMemo(() => {
return compute(deferredA, deferredB, deferredC);
}, [deferredA, deferredB, deferredC]);
}
// 优化:合并延迟
function Optimized() {
const [state, setState] = useState({ a: '', b: '', c: '' });
const deferredState = useDeferredValue(state);
const result = useMemo(() => {
return compute(deferredState.a, deferredState.b, deferredState.c);
}, [deferredState]);
}常见问题
Q1: useDeferredValue和useTransition的区别?
A: useDeferredValue是被动延迟值;useTransition是主动标记更新。前者适合外部props,后者适合内部state。
Q2: 延迟值何时更新?
A: 在所有紧急更新完成后,React空闲时更新。
Q3: 可以延迟函数吗?
A: 不推荐,因为函数引用变化会导致频繁"延迟"。
Q4: 如何检测值是否stale?
A: 比较原始值和延迟值:const isStale = value !== deferredValue
Q5: 能否取消延迟更新?
A: 不能直接取消,但可以触发新的更新覆盖。
Q6: 对SEO有影响吗?
A: 无影响,SSR时会同步渲染最终状态。
Q7: 是否需要清理?
A: 不需要,React自动管理。
Q8: 可以嵌套使用吗?
A: 可以,但通常不必要且可能降低性能。
Q9: 与setTimeout的区别?
A: useDeferredValue由React调度,更智能;setTimeout是固定延迟。
Q10: 如何测试?
A: 使用waitFor等待延迟值更新,或使用act包裹。
总结
核心概念
1. useDeferredValue用途
✅ 延迟非关键UI更新
✅ 保持输入响应性
✅ 优化渲染性能
✅ 智能调度更新
2. 适用场景
✅ 搜索/过滤
✅ 实时预览
✅ 图表更新
✅ 大列表渲染
3. 与防抖对比
✅ 智能调度 vs 固定延迟
✅ 可中断 vs 不可中断
✅ React集成 vs 独立工具
✅ UI优化 vs 逻辑控制最佳实践
1. 选择合适的延迟对象
✅ 延迟耗时渲染的值
✅ 不延迟关键交互
✅ 优先使用基本类型
✅ 稳定对象引用
2. 性能优化
✅ 配合useMemo使用
✅ 避免过度延迟
✅ 监控延迟时长
✅ 提供视觉反馈
3. 用户体验
✅ 显示stale状态
✅ 保持交互响应
✅ 平滑过渡动画
✅ 合理的加载提示useDeferredValue是React并发模式的重要组成部分,正确使用能显著提升应用的响应性和用户体验。