createRoot
允许在浏览器的 DOM 节点中创建根节点以显示 React 组件。
const root = createRoot(domNode, options?)
createRoot(domNode, options?)
调用
createRoot
以在浏览器 DOM 元素中创建根节点显示内容。
import { createRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = createRoot(domNode);
React 将会为
domNode
创建一个根节点,并控制其中的 DOM。在已经创建根节点之后,需要调用
root.render
来显示 React 组件:
root.render(<App />);
对于一个完全用 React 构建的应用程序,通常会调用一个
createRoot
来创建它的根节点。而对于一个使用了“少量” React 来创建部分内容的应用程序,则要按具体需求来确定根节点的数量。
参见下方更多示例 。
domNode
:一个
DOM 元素
。React 将为这个 DOM 元素创建一个根节点然后允许你在这个根节点上调用函数,比如
render
来显示渲染的 React 内容。
可选
options
:用于配置这个 React 根节点的对象。
onCaughtError
: Callback called when React catches an error in an Error Boundary. Called with the
error
caught by the Error Boundary, and an
errorInfo
object containing the
componentStack
.
onUncaughtError
: Callback called when an error is thrown and not caught by an Error Boundary. Called with the
error
that was thrown, and an
errorInfo
object containing the
componentStack
.
onRecoverableError
: Callback called when React automatically recovers from errors. Called with an
error
React throws, and an
errorInfo
object containing the
componentStack
. Some recoverable errors may include the original error cause as
error.cause
.
identifierPrefix
: A string prefix React uses for IDs generated by
useId
.
Useful to avoid conflicts when using multiple roots on the same page.
createRoot
返回一个带有两个方法的的对象,这两个方法是:
render
和
unmount
。
createRoot()
。请使用
hydrateRoot()
替代它。
createRoot
。但如果你使用了框架,它可能已经自动帮你完成了这次调用。
createPortal
替代
createRoot
。
root.render(reactNode)
调用
root.render
以将一段
JSX
(“React 节点”)在 React 的根节点中渲染为 DOM 节点并显示。
root.render(<App />);
React 将会在
根节点
中显示
<App />
组件,并且控制组件中的 DOM。
参见下方更多示例 。
reactNode
:一个你想要显示的
React 节点
。它总是一段 JSX,就像
<App />
,但是你也总是可以传递一个
createElement()
构造的 React 元素、一个字符串、一个数字、
null
或者
undefined
。
root.render
返回
undefined
。
首次调用
root.render
时,React 会先清空根节点中所有已经存在的 HTML,然后才会渲染 React 组件。
如果你的根节点包含了由 React 在构建期间通过服务端渲染生成的 HTML 内容,请使用
hydrateRoot()
替代这个方法,这样才能把事件处理程序和现有的 HTML 绑定。
如果你在一个根节点上多次调用了
render
,React 仍然会更新 DOM,这样才能保证显示的内容是最新的。React 将会筛选出可复用的部分和需要更新的部分,对于需要更新的部分,是 React 通过与之前渲染的树进行
“比较”
得到的。在同一个根节点上再次调用
render
就和在根节点上调用
set
函数
类似:React 会避免没必要的 DOM 更新。
Although rendering is synchronous once it starts,
root.render(...)
is not. This means code after
root.render()
may run before any effects (
useLayoutEffect
,
useEffect
) of that specific render are fired. This is usually fine and rarely needs adjustment. In rare cases where effect timing matters, you can wrap
root.render(...)
in
flushSync
to ensure the initial render runs fully synchronously.
const root = createRoot(document.getElementById('root'));
root.render(<App />);
// 🚩 The HTML will not include the rendered <App /> yet:
console.log(document.body.innerHTML);
root.unmount()
调用
root.unmount
以销毁 React 根节点中的一个已经渲染的树。
root.unmount();
通常情况下,一个完全由 React 构建的应用程序不会调用
root.unmount
。
此方法适用的场景是,React 根节点中的 DOM 节点(或者它的任何一个父级节点)被除了这个方法以外的代码移除了。举个例子,试想在一个 jQuery 选项卡面板中,非活跃状态的选项卡的 DOM 结构将被移除。一个标签页被移除时,它内部的所有内容(包括 React 根节点)也将会从 DOM 树移除。在这种情况下,你才需要调用
root.unmount
来通知 React “停止”控制已经被移除的根节点的内容。否则,被移除的根节点的内部组件就不能及时释放消息订阅等资源。
调用
root.unmout
将卸载根节点内的所有组件,该根节点上的 React 将被剥离,即所有事件处理程序以及组件树上的状态将被移除。
root.unmount
不接收任何参数。
root.unmount
返回
undefined
。
调用
root.unmout
将卸载根节点内的所有组件,该根节点上的 React 将被剥离,即所有事件处理程序以及组件树上的状态将被移除。
一旦调用
root.unmout
,就不能在该根节点上调用
root.render
。在一个已经卸载的根节点上尝试调用
root.render
将会抛出异常错误信息“无法更新一个未挂载的根节点(Cannot update an unmouted root)”。不过,你可以在卸载一个根节点后又重新创建它。
渲染一个完全由 React 构建的应用
如果应用程序是完全由 React 构建的,那么请为整个应用程序创建全局唯一的一个根节点。
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
通常情况下,在项目启动阶段,你只需要运行下面的代码。它将会:
import { createRoot } from 'react-dom/client'; import App from './App.js'; import './styles.css'; const root = createRoot(document.getElementById('root')); root.render(<App />);如果你的应用程序完全由 React 构建,你仅应该创建全局唯一的一个根节点,并只调用一次
root.render
。从这时起,React 将会控制整个应用程序的 DOM。如果要添加更多组件,可以将它们嵌套进
App
组件中。如果你需要更新视图,每一个组件都可以通过使用 state 做到这一点。如果你需要额外显示一些在这个 DOM 节点之外的内容,比如一个弹窗或者提示框,那么可以 使用 portal 进行渲染。渲染一个部分由 React 构建的应用
如果页面 不完全是 React 构建的,可以多次调用
createRoot
为每一个由 React 管理的顶级视图片段,即一段 DOM 中的顶级节点,创建一个根节点。可以在每一个根节点中调用root.render
来显示不同的内容。这样依赖,两个不同的 React 组件分别在同一个文件
index.html
内定义的两个 DOM 节点中被渲染:
import './styles.css'; import { createRoot } from 'react-dom/client'; import { Comments, Navigation } from './Components.js'; const navDomNode = document.getElementById('navigation'); const navRoot = createRoot(navDomNode); navRoot.render(<Navigation />); const commentDomNode = document.getElementById('comments'); const commentRoot = createRoot(commentDomNode); commentRoot.render(<Comments />);也可以通过
document.createElement()
创建一个新的 DOM 节点然后手动将其加入页面文档之中。
const domNode = document.createElement('div');const root = createRoot(domNode);root.render(<Comment />);document.body.appendChild(domNode); // 你可以把它加入到页面文档的任何位置调用
root.unmount
将从 DOM 节点移除这个 React 树并清除它使用的资源。
root.unmount();如果 React 组件处于一个由不同框架构建的应用程序之中时,那么这个方法将会非常有用。
更新一个根组件
可以在同一个根节点上多次调用
render
。只要当前的组件树结构与之前渲染的结果是一致的,React 将会 保存 state。仔细思考一下,为什么能在输入框中正常输入?正如下方的示例,在每一秒中内多次重复调用render
后发生的更新,是非破坏性的:
import { createRoot } from 'react-dom/client'; import './styles.css'; import App from './App.js'; const root = createRoot(document.getElementById('root')); let i = 0; setInterval(() => { root.render(<App counter={i} />); i++; }, 1000);多次调用
render
是一个不常见的事情。通常情况下,你的组件将通过 更新 state 达到同样的效果。Error logging in production
By default, React will log all errors to the console. To implement your own error reporting, you can provide the optional error handler root options
onUncaughtError
,onCaughtError
andonRecoverableError
:
import { createRoot } from "react-dom/client";import { reportCaughtError } from "./reportError";const container = document.getElementById("root");const root = createRoot(container, {onCaughtError: (error, errorInfo) => {if (error.message !== "Known error") {reportCaughtError({error,componentStack: errorInfo.componentStack,});}},});The onCaughtError option is a function called with two arguments:
Together with onUncaughtError
and onRecoverableError
, you can can implement your own error reporting system:
import { createRoot } from "react-dom/client"; import App from "./App.js"; import { onCaughtErrorProd, onRecoverableErrorProd, onUncaughtErrorProd, } from "./reportError"; const container = document.getElementById("root"); const root = createRoot(container, { // Keep in mind to remove these options in development to leverage // React's default handlers or implement your own overlay for development. // The handlers are only specfied unconditionally here for demonstration purposes. onCaughtError: onCaughtErrorProd, onRecoverableError: onRecoverableErrorProd, onUncaughtError: onUncaughtErrorProd, }); root.render(<App />);我已经创建了一个根节点,但是页面没有显示任何内容
确保你没有忘记在根节点上 渲染 你的应用程序:
import { createRoot } from 'react-dom/client';import App from './App.js';const root = createRoot(document.getElementById('root'));root.render(<App />);在你进行渲染之前,页面不会显示任何内容。
I’m getting an error: “You passed a second argument to root.render”
A common mistake is to pass the options for
createRoot
toroot.render(...)
:ConsoleWarning: You passed a second argument to root.render(…) but it only accepts one argument.To fix, pass the root options to
createRoot(...)
, notroot.render(...)
:
// 🚩 Wrong: root.render only takes one argument.root.render(App, {onUncaughtError});// ✅ Correct: pass options to createRoot.const root = createRoot(container, {onUncaughtError});root.render(<App />);I’m getting an error: “Target container is not a DOM element”
这个异常错误即字面意思,你传递给
createRoot
的内容不是一个 DOM 元素。如果你不确定发生了什么,试着在控制台打印它:
const domNode = document.getElementById('root');console.log(domNode); // ???const root = createRoot(domNode);root.render(<App />);举个例子,如果
domNode
是null
,代表着getElementById
返回了null
。这意味着你在调用这个方法的时候,页面文档内并不存在指定的 id 对应的元素,于是就出现了这个问题。这里有一些可能的原因:你使用的 id 可能和 HTML 文件中的 id 不同。请检查一下你的拼写是否正确! 打包构建产物的 HTML 文件中的 <script>
标签,不能感知到在它执行 之后 才出现的 DOM 节点。触发这个异常报错的另一个常见方式是,将
createRoot(domNode)
错写成createRoot(<App />)
。我得到了一个异常报错信息:“函数不是合法的 React 子项(Functions are not valid as a React child)”
这个错误意味着,你传递给
root.render
的内容不是一个 React 组件。这可能发生在你使用
Component
而不是<Component />
作为参数对root.render
进行调用时:
// 🚩 错误方式:App 是一个函数,不是一个组件。root.render(App);// ✅ 正确方式:<App /> 是一个组件。root.render(<App />);或者你向
root.render
传递了一个函数本身,而不是其返回值:
// 🚩 错误方式:createApp 是一个函数,不是一个组件。root.render(createApp);// ✅ 正确方式:使用 createApp 执行后返回的组件。root.render(createApp());