DrillLab
第 76 / 105 道76 / 105 · #344

React 18 有哪些新变化

What are the new changes in react 18

先自己答,再往下看Answer it yourself first

一句话:核心是并发渲染(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 setState calls in a setTimeout, 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 APIcreateRoot replaces ReactDOM.render, and until you switch you get none of the concurrent features.
  • Suspense works on the server — streaming and selective hydration.
    Plus useId (ids that match between server and client) and useSyncExternalStore (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).

JSX两个最能感知的变化示意Illustrative
1// React 17:这里会渲染两次
2setTimeout(() => {
3 setA(1);
4 setB(2);
5}, 0);
6// React 18:只渲染一次(自动批处理)
7
8// useTransition:输入不卡,列表慢慢跟
9const [isPending, startTransition] = useTransition();
10
11function onChange(e) {
12 setQuery(e.target.value); // 紧急:输入框马上响应
13 startTransition(() => setList(filter(e.target.value))); // 不急
14}
15{isPending && <span>更新中…</span>}
1// React 17: this renders twice
2setTimeout(() => {
3 setA(1);
4 setB(2);
5}, 0);
6// React 18: renders once (automatic batching)
7
8// useTransition: the input stays responsive and the list catches up
9const [isPending, startTransition] = useTransition();
10
11function onChange(e) {
12 setQuery(e.target.value); // urgent: the input responds at once
13 startTransition(() => setList(filter(e.target.value))); // not urgent
14}
15{isPending && <span>Updating</span>}