React 18 有哪些新变化
What are the new changes in react 18
一句话:核心是并发渲染(concurrent rendering)—— React 可以中断、暂停、恢复一次渲染, 好让高优先级的更新先跑。
五个具体变化(挑三四个说清就够):
- 自动批处理(automatic batching)—— React 17 只在 React 事件回调里批处理,
setTimeout、 Promise、原生事件里的多次setState会各触发一次渲染。18 里全都批处理了。这是最容易被观察到的变化。 useTransition—— 把一个更新标记为「不着急」。 典型场景:输入框旁边有个很重的搜索结果列表,输入保持流畅,列表慢慢跟上。useDeferredValue—— 同一个目的, 但是从「值」的角度:给我一个滞后版本的值。- 新的 root API——
createRoot取代ReactDOM.render,不换它就用不上任何并发特性。 - Suspense 支持 SSR—— 流式渲染、选择性 hydration。
另外useId(服务端客户端一致的 id)、useSyncExternalStore(给状态库用的)。
会追问:「StrictMode 在 18 里有什么变化?」——会额外「挂载 → 卸载 → 再挂载」一次, 所以 effect 的清理函数会被执行。这是为了提前暴露「没写清理函数」的组件(见 #332),为将来的 Offscreen 特性做准备。
「升级要注意什么?」—— 换 createRoot; 自动批处理可能让依赖「每次 setState 都立刻渲染」 的老代码行为改变 (必要时用 flushSync 逃出批处理)。
In one line: the core is concurrent rendering — React can interrupt, pause and resume a render so a higher-priority update goes first.
Five concrete changes — covering three or four properly is enough:
- Automatic batching — React 17 only batched inside React event handlers, so several
setStatecalls in asetTimeout, a Promise or a native event each triggered their own render. 18 batches all of them. This is the change you notice most easily. useTransition— mark an update as “not urgent”. The classic case is an input next to a heavy list of search results: typing stays smooth and the list catches up.useDeferredValue— same goal, from the value side: give me a version of this value that lags behind.- The new root API —
createRootreplacesReactDOM.render, and until you switch you get none of the concurrent features. - Suspense works on the server — streaming and selective hydration.
PlususeId(ids that match between server and client) anduseSyncExternalStore(for state libraries).
Follow-up: “What changed for StrictMode in 18?” — it now does an extra mount, unmount, mount, so effect cleanup functions actually run. The point is to expose components that forgot to write cleanup (see #332) and to prepare for the future Offscreen feature.
“What do you watch out for when upgrading?” — switch to createRoot; automatic batching can change the behaviour of old code that assumed “every setState renders right away” (use flushSync to escape a batch when you truly need to).