createRoot
允许您创建一个根节点,用于在浏览器 DOM 节点内显示 React 组件。
const root = createRoot(domNode, options?)
参考
createRoot(domNode, options?)
调用 createRoot
创建一个 React 根节点,用于在浏览器 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 根节点选项的对象。- 可选
onRecoverableError
:在 React 自动从错误中恢复时调用的回调函数。使用 React 抛出的error
和包含componentStack
的errorInfo
对象调用。某些可恢复的错误可能包含原始错误原因,如error.cause
。 - 可选
identifierPrefix
:React 用于useId
生成的 ID 的字符串前缀。在同一页面上使用多个根目录时,这有助于避免冲突。
返回
createRoot
返回一个包含两个方法的对象:render
和 unmount
。
注意事项
- 如果您的应用程序是服务器渲染的,则不支持使用
createRoot()
。请改用hydrateRoot()
。 - 您的应用程序中可能只有一个
createRoot
调用。如果您使用的是框架,它可能会为您执行此调用。 - 如果您想在 DOM 树中不是组件子级的其他部分渲染 JSX 片段(例如,模态框或工具提示),请使用
createPortal
而不是createRoot
。
root.render(reactNode)
调用 root.render
以在 React 根目录的浏览器 DOM 节点中显示一段 JSX(“React 节点”)。
root.render(<App />);
React 会在 root
中显示 <App />
,并接管对其内部 DOM 的管理。
参数
reactNode
:您要显示的_React 节点_。这通常是一段 JSX 代码,如<App />
,但您也可以传递使用createElement()
构造的 React 元素、字符串、数字、null
或undefined
。
返回值
root.render
返回 undefined
。
注意事项
-
第一次调用
root.render
时,React 会在将 React 组件渲染到 React 根节点之前,清除该节点中现有的所有 HTML 内容。 -
如果根节点的 DOM 节点包含由服务器端 React 或构建过程中生成的 HTML,请改用
hydrateRoot()
,它会将事件处理程序附加到现有的 HTML。 -
如果在同一个根节点上多次调用
render
,React 将根据需要更新 DOM 以反映传递的最新 JSX。React 会通过将当前树与之前渲染的树进行 “匹配” 来决定 DOM 的哪些部分可以复用,哪些部分需要重新创建。在同一个根节点上再次调用render
类似于在根组件上调用set
函数:React 会避免不必要的 DOM 更新。
root.unmount()
调用 root.unmount
以销毁 React 根节点中已渲染的树。
root.unmount();
完全使用 React 构建的应用程序通常不会调用 root.unmount
。
如果 React 根节点的 DOM 节点(或其任何祖先节点)可能被其他代码从 DOM 中移除,则此方法非常有用。例如,假设一个 jQuery 选项卡面板会从 DOM 中移除非活动选项卡。如果一个选项卡被移除,则其中的所有内容(包括 React 根节点)也将从 DOM 中移除。在这种情况下,需要通过调用 root.unmount
来告诉 React “停止”管理已移除根节点的内容。否则,已移除根节点中的组件将不知道要清理和释放全局资源,例如订阅。
调用 root.unmount
将卸载根节点中的所有组件,并将 React 从根 DOM 节点中“分离”,包括移除树中的任何事件处理程序或状态。
参数
root.unmount
不接受任何参数。
返回值
root.unmount
返回 undefined
。
注意事项
-
调用
root.unmount
会卸载树中的所有组件,并将 React 从根 DOM 节点“分离”。 -
调用
root.unmount
后,不能在同一个根节点上再次调用root.render
。尝试在已卸载的根节点上调用root.render
将抛出“无法更新已卸载的根节点”错误。但是,可以在为同一个 DOM 节点创建的上一个根节点被卸载后,为该节点创建一个新的根节点。
用法
渲染完全使用 React 构建的应用程序
如果应用程序完全使用 React 构建,请为整个应用程序创建一个根节点。
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
通常,只需要在启动时运行一次此代码。它将
- 查找 HTML 中定义的 浏览器 DOM 节点。
- 在其中显示应用程序的 React 组件。
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
组件内。 当您需要更新 UI 时,您的每个组件都可以通过使用状态来实现。 当您需要在 DOM 节点之外显示额外内容(如模态框或工具提示)时,请使用传送门渲染它。
渲染部分使用 React 构建的页面
如果您的页面并非完全使用 React 构建,您可以多次调用 createRoot
为 React 管理的每个顶级 UI 片段创建一个根节点。您可以通过调用 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); // You can add it anywhere in the document
要从 DOM 节点中移除 React 树并清理其使用的所有资源,请调用 root.unmount
。
root.unmount();
如果您的 React 组件位于使用其他框架编写的应用程序中,这将非常有用。
更新根组件
您可以在同一个根节点上多次调用 render
。只要组件树结构与之前渲染的内容相匹配,React 就会保留状态。请注意,您可以在输入框中输入内容,这意味着在本例中,每秒重复调用 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
并非常见。通常,您的组件会更新状态。
为未捕获的错误显示对话框
默认情况下,React 会将所有未捕获的错误记录到控制台。要实现您自己的错误报告,您可以提供可选的 onUncaughtError
根选项。
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onUncaughtError: (error, errorInfo) => {
console.error(
'Uncaught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);
onUncaughtError 选项是一个函数,它接收两个参数:
- 抛出的 error。
- 一个 errorInfo 对象,其中包含错误的 componentStack。
您可以使用 onUncaughtError
根选项来显示错误对话框。
import { createRoot } from "react-dom/client"; import App from "./App.js"; import {reportUncaughtError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = createRoot(container, { onUncaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportUncaughtError({ error, componentStack: errorInfo.componentStack }); } } }); root.render(<App />);
显示错误边界错误
默认情况下,React 会将错误边界捕获的所有错误记录到 console.error
。要覆盖此行为,您可以提供可选的 onCaughtError
根选项来处理 错误边界 捕获的错误。
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onCaughtError: (error, errorInfo) => {
console.error(
'Caught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);
onCaughtError 选项是一个函数,它接收两个参数:
- 边界捕获的 error。
- 一个 errorInfo 对象,其中包含错误的 componentStack。
您可以使用 onCaughtError
根选项来显示错误对话框或过滤已知错误以进行日志记录。
import { createRoot } from "react-dom/client"; import App from "./App.js"; import {reportCaughtError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = createRoot(container, { onCaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportCaughtError({ error, componentStack: errorInfo.componentStack, }); } } }); root.render(<App />);
为可恢复错误显示对话框
React 可能会自动渲染组件两次,尝试从渲染中抛出的错误中恢复。如果成功,React 会将可恢复错误记录到控制台以通知开发人员。要覆盖此行为,您可以提供可选的 onRecoverableError
根选项。
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onRecoverableError: (error, errorInfo) => {
console.error(
'Recoverable error',
error,
error.cause,
errorInfo.componentStack,
);
}
}
);
root.render(<App />);
onRecoverableError 选项是一个函数,它接收两个参数:
- React 抛出的 error。某些错误可能包含原始原因,如 error.cause。
- 一个包含错误 componentStack 的 errorInfo 对象。
您可以使用 onRecoverableError
根选项来显示错误对话框。
import { createRoot } from "react-dom/client"; import App from "./App.js"; import {reportRecoverableError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = createRoot(container, { onRecoverableError: (error, errorInfo) => { reportRecoverableError({ error, cause: error.cause, componentStack: errorInfo.componentStack, }); } }); root.render(<App />);
故障排除
我已经创建了一个根,但什么也没显示
确保您没有忘记将您的应用程序实际*渲染*到根目录中。
import { createRoot } from 'react-dom/client';
import App from './App.js';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
在您这样做之前,不会显示任何内容。
我收到一个错误:“您向 root.render 传递了第二个参数”
一个常见的错误是将 createRoot
的选项传递给 root.render(...)
。
要修复此问题,请将根选项传递给 createRoot(...)
,而不是 root.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 />);
我收到一个错误:“目标容器不是 DOM 元素”
此错误意味着您传递给 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 不同。检查是否有错别字!
- 您的捆绑包的
<script>
标签无法“看到”HTML 中*在其之后*出现的任何 DOM 节点。
导致此错误的另一个常见原因是编写了 createRoot(<App />)
而不是 createRoot(domNode)
。
我收到一个错误:“函数作为 React 子级无效。”
此错误意味着您传递给 root.render
的内容不是 React 组件。
如果您使用 Component
而不是 <Component />
调用 root.render
,则可能会发生这种情况。
// 🚩 Wrong: App is a function, not a Component.
root.render(App);
// ✅ Correct: <App /> is a component.
root.render(<App />);
或者,如果您将函数传递给 root.render
,而不是调用它的结果
// 🚩 Wrong: createApp is a function, not a component.
root.render(createApp);
// ✅ Correct: call createApp to return a component.
root.render(createApp());
我的服务器渲染的 HTML 从头开始重新创建
如果您的应用程序是服务器渲染的,并且包含 React 生成的初始 HTML,您可能会注意到创建根目录并调用 root.render
会删除所有 HTML,然后从头开始重新创建所有 DOM 节点。这可能会更慢,重置焦点和滚动位置,并且可能会丢失其他用户输入。
服务器渲染的应用程序必须使用 hydrateRoot
而不是 createRoot
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(
document.getElementById('root'),
<App />
);
请注意,它的 API 不同。特别是,通常不会再有 root.render
调用。