React 18 并发模式
约 801 字大约 3 分钟
布欧-Lewyon
2026-05-15
React 18 是近年来最大的一次更新,引入了并发渲染(Concurrent Rendering)——让 React 可中断渲染以优先处理高优更新。
createRoot
React 18 的新 Root API:
// React 17(旧)
import ReactDOM from 'react-dom'
ReactDOM.render(<App />, document.getElementById('root'))
// React 18(新)
import ReactDOM from 'react-dom/client'
const root = ReactDOM.createRoot(document.getElementById('root')!)
root.render(<App />)Automatic Batching
React 18 默认自动批处理——多个 setState 合并为一次重渲染。
function AutoBatch() {
const [count, setCount] = useState(0)
const [flag, setFlag] = useState(false)
const handleClick = () => {
// React 17:两次独立渲染(两次闪烁)
// React 18:自动合并,一次渲染
setCount(c => c + 1)
setFlag(f => !f)
}
// 异步函数中也会批处理(React 17 不会)
const handleAsync = () => {
fetch('/api').then(() => {
setCount(c => c + 1) // React 18: 批处理
setFlag(f => !f) // React 18: 批处理
})
}
return <button onClick={handleClick}>Count: {count}</button>
}手动退出批处理
import { flushSync } from 'react-dom'
function handleClick() {
flushSync(() => setCount(c => c + 1)) // 立即渲染
flushSync(() => setFlag(f => !f)) // 再次立即渲染
}Transitions(过渡更新)
区分紧急更新(输入)和非紧急更新(搜索结果过滤)。
import { useTransition } from 'react'
function SearchPage() {
const [query, setQuery] = useState('')
const [results, setResults] = useState<string[]>([])
const [isPending, startTransition] = useTransition()
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// 紧急更新:输入框响应
setQuery(e.target.value)
// 非紧急更新:搜索结果(可中断)
startTransition(() => {
const filtered = expensiveFilter(allItems, e.target.value)
setResults(filtered)
})
}
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />} {/* 过渡中显示加载 */}
<ResultsList items={results} />
</div>
)
}useDeferredValue
useDeferredValue 让某个值的更新延迟,让紧急更新先完成。
import { useDeferredValue, useMemo } from 'react'
function Search({ query }: { query: string }) {
// query 的延迟版本(紧急输入更新优先)
const deferredQuery = useDeferredValue(query)
const isStale = query !== deferredQuery
const results = useMemo(() =>
expensiveFilter(allItems, deferredQuery),
[deferredQuery]
)
return (
<div style={{ opacity: isStale ? 0.5 : 1 }}>
{results.map(item => <Item key={item.id} item={item} />)}
</div>
)
}Suspense 改进
React 18 中 Suspense 支持服务端渲染和数据获取。
import { Suspense, lazy } from 'react'
const Comments = lazy(() => import('./Comments'))
function PostPage() {
return (
<div>
<PostContent />
<Suspense fallback={<Loading />}>
<Comments /> {/* 数据加载完成前显示 Loading */}
</Suspense>
</div>
)
}新 Hooks
// useId —— 生成唯一 ID,服务端渲染友好
function FormField({ label }: { label: string }) {
const id = useId()
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} />
</div>
)
}
// useSyncExternalStore —— 外部 Store 同步
import { useSyncExternalStore } from 'react'
function useOnlineStatus() {
const isOnline = useSyncExternalStore(
callback => { window.addEventListener('online', callback)
window.addEventListener('offline', callback)
return () => {
window.removeEventListener('online', callback)
window.removeEventListener('offline', callback)
} },
() => navigator.onLine,
() => true // SSR fallback
)
return isOnline
}
// useInsertionEffect —— CSS-in-JS 库用
// 在 DOM 变更前同步执行,比 useEffect 早升级行为变更
1. Strict Mode 开发环境双次渲染(检测副作用)
2. 组件 unmount 后又重新 mount(为未来组件复用)
3. setState 在 unmount 后不再有 warning
4. React DOM 组件 props 类型更严格升级清单
1. ReactDOM.createRoot 替代 ReactDOM.render
2. 移除 unsafe lifecycle 和 componentWillMount 等
3. TypeScript 类型更新(@types/react 18.x)
4. 测试自动批处理行为(特别是异步 setState)
5. Suspense + lazy 替代手动代码分割小结
createRoot+Automatic Batching是 React 18 的基础升级。useTransition/useDeferredValue让 UI 保持响应(输入不卡顿)。- Suspense 改进:支持服务端渲染和数据获取。
useId解决 SSR 和服务端/客户端不匹配问题。useSyncExternalStore让 Zustand/Redux 等外部 Store 在并发模式下安全。
上一节:React 16 → 17 渐进式升级 下一节:React 19 新特性
