createRoot
用于在浏览器DOM节点内显示React组件。
const root = createRoot(domNode, options?)
参考
createRoot(domNode, options?)
调用createRoot
可在浏览器DOM元素内创建React根,用于显示内容。
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
: 当React在错误边界中捕获错误时调用的回调函数。使用错误边界捕获的error
和包含componentStack
的errorInfo
对象调用。 - 可选
onUncaughtError
:当抛出错误且未被错误边界捕获时调用的回调函数。调用时传入抛出的error
以及包含componentStack
的errorInfo
对象。 - 可选
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
将一段JSX(“React 节点”)显示到 React 根节点的浏览器 DOM 节点中。
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中移除。在这种情况下,你需要告诉React通过调用root.unmount
来“停止”管理已移除根节点的内容。否则,已移除根节点内的组件将不知道如何清理并释放全局资源(如订阅)。
调用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节点外部显示额外内容(如模态框或工具提示)时,使用portal进行渲染。
部分使用 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.cause。
- 一个包含错误组件堆栈的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 不同。检查是否有错别字!
- 您的 bundle 的
<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
调用。