React 20 的併發特性不再是實驗性功能。useTransition、useDeferredValue 和新的排程 API 全部進入穩定版,並且在底層做了大量效能最佳化。對於需要處理大量使用者互動的複雜應用,這些特性終於可以放心用了。
useTransition 的實際應用
useTransition 最常見的用法是讓路由切換或大數據過濾不阻塞輸入。React 20 優化了 transition 的排程粒度,低優先順序更新不再被高優先順序長時間餓死。
javascript
import { useState, useTransition, Suspense } from 'react';
function SearchableProductList({ products }) {
const [filter, setFilter] = useState('');
const [isPending, startTransition] = useTransition();
const handleFilter = (e) => {
// 立即更新輸入框(高優先順序)
setFilter(e.target.value);
// 過濾計算放到 transition 中(低優先順序)
startTransition(() => {
setFilteredProducts(
products.filter(p =>
p.name.toLowerCase().includes(e.target.value.toLowerCase())
)
);
});
};
const filtered = products.filter(p =>
p.name.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
<input
type="text"
value={filter}
onChange={handleFilter}
placeholder="搜尋產品..."
/>
<div style={{ opacity: isPending ? 0.7 : 1, transition: 'opacity 150ms' }}>
<ProductGrid products={filtered} />
</div>
{isPending && <Spinner position="top-right" />}
</div>
);
}
React 20 的改進在於:transition 內的狀態更新可以中斷和恢復。當你連續快速輸入時,React 會丟棄中間的過時計算,只執行最後一次。
useDeferredValue 的自動記憶化
useDeferredValue 在 React 20 中獲得了編譯器最佳化的支援。它不再簡單地延遲值傳遞,而是與 React Compiler 協作,只重新渲染真正需要更新的子樹。
javascript
import { useState, useDeferredValue, memo } from 'react';
// React 20 Compiler 會自動最佳化這個元件
// 不需要手動寫 memo
function ProductGrid({ products }) {
return (
<div className="grid">
{products.map(p => (
<ProductCard key={p.id} product={p} />
))}
</div>
);
}
function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// 當 query 變化時,input 立即更新
// filteredProducts 使用 deferredQuery,更新被延遲
const filteredProducts = useMemo(
() => filterProducts(deferredQuery),
[deferredQuery]
);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<ProductGrid products={filteredProducts} />
</div>
);
}
關鍵變化是 useDeferredValue 現在支援自定義比較函式:
javascript
// React 20 新增:自定義比較邏輯
const deferredQuery = useDeferredValue(query, {
// 只有當值變化超過 300ms 才認為是新的 deferred 值
timeoutMs: 300,
// 自定義相等性檢查
isEqual: (prev, next) => prev.trim() === next.trim(),
});
Scheduler API:自定義優先順序
React 20 暴露了底層排程 API,讓你可以為特定更新指定優先順序。這在複雜互動場景中非常有用:
javascript
import { unstable_scheduleUpdate, Priority } from 'react-scheduler';
function DragDropBoard() {
const handleDrag = (item, position) => {
// 拖拽時的即時反饋:最高優先順序
unstable_scheduleUpdate({
priority: Priority.Immediate,
task: () => updateDragPosition(item, position),
});
};
const handleDrop = (item, targetList) => {
// 放下後的資料同步:普通優先順序
unstable_scheduleUpdate({
priority: Priority.Normal,
task: () => persistDropOperation(item, targetList),
});
// 後臺統計更新:最低優先順序
unstable_scheduleUpdate({
priority: Priority.Idle,
task: () => trackAnalytics('item_moved', { item, targetList }),
});
};
return <Board onDrag={handleDrag} onDrop={handleDrop} />;
}
Priority 級別:Immediate > UserBlocking > Normal > Low > Idle。在實際專案中,建議只在拖拽、遊戲、即時協作等對延遲敏感的場景中使用自定義優先順序。
併發模式下的表單最佳化
React 20 的併發特性與新表單 API 完美配合。輸入框的即時響應和後端驗證的延遲執行天然分離:
javascript
import { useState, useTransition } from 'react';
import { useField } from 'react';
function AsyncValidationInput() {
const [isPending, startTransition] = useTransition();
const field = useField({
name: 'username',
onChange: (value) => {
// 輸入更新是同步的(高優先順序)
// 驗證在 transition 中(低優先順序,可中斷)
startTransition(() => {
validateUsername(value);
});
},
});
return (
<div>
<input {...field.inputProps} />
{isPending && <span className="hint">檢查使用者名稱可用性...</span>}
{field.error && <span className="error">{field.error}</span>}
</div>
);
}
小結
- useTransition 排程粒度更細,連續快速輸入時自動丟棄過時更新
- useDeferredValue 支援自定義比較函式和超時控制,與 Compiler 協作最佳化渲染
- Scheduler API 暴露底層優先順序控制,適合拖拽/遊戲等高互動場景
- 併發特性與 Actions v2、新表單 API 深度整合,形成完整的響應式體系
- 併發模式不再是「實驗性功能」,而是 React 20 處理複雜互動的標準方案