Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
基于优先级的更新 — 使用 startTransition 区分紧急与非紧急渲染。
useTransition 是一个 React 18 hook,让你可以将 state 更新标记为"非紧急过渡"。紧急更新(打字、点击)立即处理,永不中断。过渡更新优先级较低 — 如果在过渡进行时紧急更新到达,React 中断过渡,处理紧急工作,然后重新开始过渡。这使 UI 即使在繁重的重新渲染期间也保持响应。
React 调度器为每个更新分配优先级通道:SyncLane(紧急,不可批处理)、InputContinuousLane(拖拽/滚动)、DefaultLane(普通 setState)和 TransitionLane(useTransition / startTransition)。高优先级通道始终抢占低优先级通道。
在 startTransition(callback) 中包裹 setState 调用告诉 React:'这个更新是过渡 — 可以被中断'。React 在 TransitionLane 中调度它,并保持当前 UI 可见,直到过渡完成。
如果用户在 5000 个项目的列表作为过渡重新渲染时输入另一个字符,React 在树中途暂停过渡渲染,立即处理打字(紧急),更新输入,然后以新输入值从头重新开始过渡。
useTransition 返回 [isPending, startTransition]。isPending 在 React 仍处理过渡更新时为 true。用它来显示微妙的加载指示器而不阻塞 UI。
startTransition 的替代方案:useDeferredValue(value) 返回在过渡期间滞后的 value 的延迟副本。UI 首先用旧的延迟值渲染(快速),然后在 React 有时间时用新值重新渲染。
过渡更新的低优先级调度通道。可以被任何更高优先级的工作(用户输入、同步更新)抢占。
将回调内的 setState 调用标记为非紧急。可作为独立导入或从 useTransition hook 使用。
来自 useTransition 的布尔值。在 React 仍处理过渡更新时为 true。用于显示 spinner 而不阻塞输入。
返回值的延迟副本。首先以陈旧值渲染(即时),在空闲时以新值重新渲染。适用于过滤大型列表。
当紧急更新在过渡中途到达时,React 丢弃正在进行的过渡渲染,并在处理紧急工作后重新开始。
1import { useState, useTransition } from 'react';23function SearchPage() {4 const [query, setQuery] = useState('');5 const [results, setResults] = useState(allItems);6 const [isPending, startTransition] = useTransition();78 function handleChange(e) {9 // Urgent: update the input immediately (never interrupted)10 setQuery(e.target.value);1112 // Non-urgent: filter 10,000 items as a transition13 startTransition(() => {14 setResults(allItems.filter(item =>15 item.name.includes(e.target.value)16 ));17 });18 }1920 return (21 <>22 <input value={query} onChange={handleChange} />23 {isPending && <Spinner />} {/* subtle loading indicator */}24 <ResultsList items={results} />25 </>26 );27}
在过渡之前,React 以相同优先级渲染所有 state 更新 — 繁重的列表重新渲染可能会冻结输入数百毫秒。过渡让你明确告诉 React '用户需要输入感觉立即响应,但结果列表可以等一会儿'。这是处理昂贵渲染时,一个卡顿的应用和一个流畅的应用之间的区别。
A dashboard has tabs: Overview, Analytics, Reports. Clicking 'Analytics' loads heavy chart components. Without transitions, the current tab disappears immediately and the user sees a loading blank until the new tab renders.
The tab switch calls `setActiveTab('analytics')`, which immediately unmounts the Overview tab and starts rendering Analytics. The heavy chart render blocks the main thread for 300ms. During this time, the user sees a blank panel — the old tab is gone, the new tab isn't ready.
Wrap the tab switch in `startTransition(() => setActiveTab('analytics'))`. React keeps showing the Overview tab (marked with `isPending` for a subtle opacity/spinner indicator) while rendering Analytics in the background. When ready, it swaps them in a single frame — no blank state.
Takeaway: useTransition is perfect for tab switching, accordion expansions, and any UI where you want the old content to stay visible while new content prepares. The `isPending` boolean lets you show a subtle loading indicator without destroying the current view.
A search results component receives `query` as a prop and does heavy filtering inline. You want to defer the results rendering but can't wrap the parent's setState in startTransition because you don't control the parent.
The parent component calls `setQuery(e.target.value)` directly. You can't add startTransition because you don't own the parent component (maybe it's from a library). Your heavy ResultsList re-renders on every keystroke because `query` prop changes immediately.
Use `useDeferredValue(query)` in ResultsList: `const deferredQuery = useDeferredValue(query)`. Filter using `deferredQuery` instead of `query`. React returns the old value during heavy renders, showing stale-but-fast results while computing the new filter in the background. Compare `query !== deferredQuery` to show a loading indicator.
Takeaway: useDeferredValue is the consumer-side equivalent of useTransition. Use useTransition when you control the state update (can wrap setState). Use useDeferredValue when you want to defer derived rendering from a prop you don't control.