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 根节点选项的对象。

    • 仅限 Canary 可选 onUncaughtError:当抛出错误且未被错误边界捕获时调用的回调函数。使用抛出的 error 和包含 componentStackerrorInfo 对象调用。
    • 可选 onRecoverableError:在 React 自动从错误中恢复时调用的回调函数。使用 React 抛出的 error 和包含 componentStackerrorInfo 对象调用。某些可恢复的错误可能包含原始错误原因,如 error.cause
    • 可选 identifierPrefix:React 用于 useId 生成的 ID 的字符串前缀。在同一页面上使用多个根目录时,这有助于避免冲突。

返回

createRoot 返回一个包含两个方法的对象:renderunmount

注意事项

  • 如果您的应用程序是服务器渲染的,则不支持使用 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 元素、字符串、数字、nullundefined

返回值

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 />);

通常,只需要在启动时运行一次此代码。它将

  1. 查找 HTML 中定义的 浏览器 DOM 节点
  2. 在其中显示应用程序的 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 节点之外显示额外内容(如模态框或工具提示)时,请使用传送门渲染它。

注意

当您的 HTML 为空时,用户会看到一个空白页面,直到应用程序的 JavaScript 代码加载并运行。

<div id="root"></div>

这可能会让人感觉很慢!为了解决这个问题,您可以在服务器上或构建期间从您的组件生成初始 HTML。 然后,您的访问者可以在加载任何 JavaScript 代码之前阅读文本、查看图像和点击链接。我们建议使用一个框架来自动完成此优化。根据其运行时间,这被称为_服务器端渲染 (SSR)_ 或_静态站点生成 (SSG)_。

陷阱

使用服务器渲染或静态生成的应用程序必须调用 hydrateRoot 而不是 createRootReact 将_hydrate_(复用)您 HTML 中的 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 并非常见。通常,您的组件会更新状态

为未捕获的错误显示对话框

Canary

onUncaughtError 仅在最新的 React Canary 版本中可用。

默认情况下,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 选项是一个函数,它接收两个参数:

  1. 抛出的 error
  2. 一个 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 />);

显示错误边界错误

Canary

onCaughtError 仅在最新的 React Canary 版本中可用。

默认情况下,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 选项是一个函数,它接收两个参数:

  1. 边界捕获的 error
  2. 一个 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 选项是一个函数,它接收两个参数:

  1. React 抛出的 error。某些错误可能包含原始原因,如 error.cause
  2. 一个包含错误 componentStackerrorInfo 对象。

您可以使用 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(...)

控制台
警告:您向 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 />);

例如,如果 domNodenull,则意味着 getElementById 返回了 null。如果在调用时文档中没有具有给定 ID 的节点,就会发生这种情况。这可能有几个原因:

  1. 您要查找的 ID 可能与您在 HTML 文件中使用的 ID 不同。检查是否有错别字!
  2. 您的捆绑包的 <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 调用。