并发 React
在以前的 React 中,渲染是不可中断的,一旦状态更新,React 就会埋头渲染(包括同步到 DOM 树),渲染完才能进行处理新的状态更新(或者做别的事)。这种情况下如果一秒内产生了上百次的状态更新,页面会直接卡死。
而在并发 React 中,渲染是可中断的,一旦状态更新,React 会先把渲染任务放到一个队列中,然后开始处理新的状态更新,等到空闲的时候再去渲染队列中的任务。这样就不会出现页面卡死的情况了。
由于并发 React 的特性,升级到 React 18 后可能会导致组件的行为发生变化。
升级到 React 18
客户端渲染
ReactDOM.render
在 18 中不再支持,但仍可以使用,它的运行方式与 17 保持一致,即不支持并发渲染。
在 18 中,应当使用 createRoot
来创建一个要渲染的根结点,然后再执行渲染:
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container); // TS 中为 `createRoot(container!)`
root.render(<App tab="home" />);
ReactDOM.unmountComponentAtNode
也同样被弃用了,使用 root.unmount()
代替。
TypeScript 支持
需升级 @types/react
和 @types/react-dom
。
状态批处理
在 17 中,React 只会对 onClick
等自身的事件监听进行状态批处理。而在 18 中,React 会对 setTimeout
、Promise
和事件监听等的执行回调进行状态批处理:
setTimeout(() => {
setCount((c) => c + 1);
setFlag((f) => !f);
// React 会把这两个状态更新合并为一个
}, 1000);
这个特性可能会改变组件的行为,如果你想照旧各自触发更新,可以用 flushSync
包裹:
import { flushSync } from 'react-dom';
setTimeout(() => {
flushSync(() => {
setCount((c) => c + 1);
});
flushSync(() => {
setFlag((f) => !f);
});
}, 1000);
Strict Mode
在升级到 18 时,可以用 <StrictMode>
来检测由于并发特性而导致的 bug ,它只在开发环境下生效。同时 18 的 <StrictMode>
自身也做了更新,它会在第一次挂载后模拟组件的卸载和重新挂载,因此被 <StrictMode>
包裹的组件可能会触发两遍 useEffect
。
其他值得注意的变动
- 组件可以渲染
undefined
了。