Skip to content

React Concurrent 特性

1. Concurrent Rendering

传统同步渲染

┌──────────────────────────────────────┐
│ 更新开始 ─────────────────> 渲染完成 │
│            (不可中断)                │
└──────────────────────────────────────┘
用户交互被阻塞 😢

并发渲染

┌──────┬──────┬──────┬──────┬──────────┐
│ 渲染 │ 让出 │ 渲染 │ 让出 │ 提交完成 │
└──────┴──────┴──────┴──────┴──────────┘
中间穿插用户交互处理 😊

2. Suspense 原理

基本用法

jsx
<Suspense fallback={<Loading />}>
    <LazyComponent />
</Suspense>

工作原理

  1. 子组件 抛出 Promise (throw thenable)
  2. Suspense 捕获 Promise,渲染 fallback
  3. Promise resolve 后,重新渲染子组件

源码简化

javascript
function updateSuspenseComponent(current, workInProgress) {
    try {
        const nextChildren = renderChild(workInProgress);
        return nextChildren;
    } catch (thrownValue) {
        if (typeof thrownValue.then === 'function') {
            // 是 Promise,显示 fallback
            thrownValue.then(retrySuspenseComponent);
            return renderFallback(workInProgress);
        }
        throw thrownValue; // 真正的错误
    }
}

数据获取模式

  • Render-as-you-fetch: 推荐,请求与渲染并行
  • Fetch-on-render: 传统,渲染时才发请求 (瀑布流)

3. useTransition

场景

用户输入触发大量重新渲染 (如搜索),希望输入保持响应。

用法

jsx
function SearchResults() {
    const [query, setQuery] = useState('');
    const [isPending, startTransition] = useTransition();
    
    const handleChange = (e) => {
        // 高优: 立即更新输入框
        setQuery(e.target.value);
        
        // 低优: 可中断的结果更新
        startTransition(() => {
            setDeferredQuery(e.target.value);
        });
    };
    
    return (
        <>
            <input value={query} onChange={handleChange} />
            {isPending && <Spinner />}
            <ResultList query={deferredQuery} />
        </>
    );
}

原理

startTransition 内的更新标记为 TransitionLane,优先级低于用户输入。


4. useDeferredValue

场景

延迟更新一个值,让 UI 保持响应。

用法

jsx
function SearchPage({ query }) {
    const deferredQuery = useDeferredValue(query);
    
    return (
        <>
            <SearchBox value={query} />
            {/* 使用 deferred 值,更新可被打断 */}
            <Suspense fallback={<Skeleton />}>
                <SearchResults query={deferredQuery} />
            </Suspense>
        </>
    );
}

与 useTransition 的区别

useTransitionuseDeferredValue
降级 更新动作降级 值本身
需包裹 setState包裹值
控制更新时机控制读取时机

5. Automatic Batching (React 18)

之前 (React 17)

javascript
// 在 setTimeout 中,每次 setState 触发一次渲染
setTimeout(() => {
    setCount(c => c + 1); // 渲染
    setFlag(f => !f);     // 渲染
}, 0);

现在 (React 18)

javascript
// 所有场景都自动批处理
setTimeout(() => {
    setCount(c => c + 1);
    setFlag(f => !f);
    // 只有一次渲染!
}, 0);

退出批处理

javascript
import { flushSync } from 'react-dom';

flushSync(() => setCount(c => c + 1)); // 立即渲染
flushSync(() => setFlag(f => !f));     // 立即渲染

前端面试知识库