在 React 18 中,ReactDOM.render
方法已经被标记为遗弃的方法了,下一个大版本应该就被移除了,现在在使用的时候会出现警告:
取而代之的,是使用 ReactDOM.createRoot
:
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);
为什么要做这件事呢?
我们之前的 render
使用是这样的:
ReactDOM.render(<App />, rootElement,);
当我们运行上面这句代码的时候,React 会为我们创建一个顶级的根节点,叫做 FiberRoot,这个根节点是绑定到 rootElement
这个 DOM 节点上:
// container 对应的就是 `ReactDOM.render` 的第一个参数
container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
这样的话,就算我们没有改变 DOM 根节点 rootElement
,当我们再想重新渲染整个应用的时候,还是要把传递 rootElement
。并且,我们的 React 应用的根节点对使用者来说是不透明的,我们无法取到这个值。
而当我们使用 createRoot
了之后呢,我们需要在第一次的时候传入 rootElement
节点:
const root = ReactDOM.createRoot(rootElement);
后续我们渲染整个应用的时候只需要调用 root
的 render
方法就好了,React 应用的根节点也向我们暴露了出来,我们就不必再把它隐式的绑定到它到对应的 DOM 节点上去了。
root.render(<App />);
React 18 最大的优化可能莫过于 Concurrent Mode 的到来了,经过 17 版本的过渡,它终于在 18 版本到来了。为了考虑老项目的兼容性,强硬的让大家升级肯定是不行的,最好的是新版本依然不痛不痒的升级。为了更大程度的推行它,也可能是单独开一个 createRoot
API 的原因。
还有一个改动可能需要注意一下,之前的 render
方法接受一个 callback
:
ReactDOM.render(container, <App />, function() {
// 初始渲染或者后续更新会调用这里
console.log('rendered').
});
但是 createRoot
移除了它,如果你还是有类似的需求,可以考虑下面几种方案其一:
1. 使用 ref
使用这种方案,会在我们的组件第一次挂载到 DOM 上去的时候调用。
function App({ callback }) {
// div 第一次挂载到 DOM 那个操作的时候会调用 callback
return (
<div ref={callback}>
<h1>Hello World</h1>
</div>
);
}
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(<App callback={() => console.log("renderered")} />);
2. 使用 requestIdleCallback
这个方法就相当于给浏览器增加了一个低优先级的任务,会在浏览器当前帧还有空闲的时候执行,具体什么时候执行也是比较未知。
ReactDOM.createRoot(rootElement).render(<App />);
requestIdleCallback(callback);
3. 使用 setTimeout
会在 React 第一次被中断任务的时候被执行。
ReactDOM.createRoot(rootElement).render(<App />);
setTimeout(callback, 0);
我觉得第一种使用 ref
的方案比较好用一点。
最后,和你一同期待 createRoot 在项目中的应用。