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和包含componentStackerrorInfo对象调用。
    • 可选 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 将一段JSX(“React 节点”)显示到 React 根节点的浏览器 DOM 节点中。

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中移除。在这种情况下,你需要告诉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 />);

通常,你只需要在启动时运行这段代码一次。它会

  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节点外部显示额外内容(如模态框或工具提示)时,使用portal进行渲染。

注意

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

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

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

陷阱

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

显示未捕获错误的对话框

默认情况下,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 />);

显示错误边界错误

默认情况下,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.cause
  2. 一个包含错误组件堆栈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(...)

控制台
警告:您向 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. 您的 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 调用。