createRoot 让你创建一个根节点,以在浏览器 DOM 节点中显示 React 组件。

const root = createRoot(domNode, options?)

参考

🌐 Reference

createRoot(domNode, options?)

调用 createRoot 来创建一个 React 根,以便在浏览器 DOM 元素中显示内容。

🌐 Call createRoot to create a React root for displaying content inside a browser DOM element.

import { createRoot } from 'react-dom/client';

const domNode = document.getElementById('root');
const root = createRoot(domNode);

React 将为 domNode 创建一个根,并接管其内部的 DOM 管理。在创建根之后,你需要调用 root.render 来在其中显示一个 React 组件:

🌐 React will create a root for the domNode, and take over managing the DOM inside it. After you’ve created a root, you need to call root.render to display a React component inside of it:

root.render(<App />);

一个完全用 React 构建的应用通常只会对其根组件有一次 createRoot 调用。一个在页面的部分使用 React “点缀” 的页面可能会根据需要有多个独立的根组件。

🌐 An app fully built with React will usually only have one createRoot call for its root component. A page that uses “sprinkles” of React for parts of the page may have as many separate roots as needed.

查看更多示例。

参数

🌐 Parameters

  • domNode:一个 DOM 元素。React 将为此 DOM 元素创建一个根,并允许你在该根上调用函数,例如 render 来显示渲染的 React 内容。
  • 可选 options:用于此 React 根的选项对象。
    • 可选 onCaughtError:当 React 在错误边界捕获到错误时调用的回调。调用时会传入被错误边界捕获的 error,以及包含 componentStackerrorInfo 对象。
    • 可选 onUncaughtError:在抛出错误且未被错误边界捕获时调用的回调。使用被抛出的 error 和包含 componentStackerrorInfo 对象调用。
    • 可选 onRecoverableError:当 React 自动从错误中恢复时调用的回调。调用时会传入一个 error React 抛出的错误,以及一个包含 componentStackerrorInfo 对象。某些可恢复的错误可能会将原始错误原因作为 error.cause
    • 可选 identifierPrefix:React 用于 useId. 生成的 ID 的字符串前缀。在同一页面上使用多个根时,这有助于避免冲突。

返回

🌐 Returns

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

注意事项

🌐 Caveats

  • 如果你的应用是服务器渲染的,则不支持使用 createRoot()。请改用 hydrateRoot()
  • 在你的应用中,你很可能只会有一个 createRoot 调用。如果你使用框架,它可能会为你执行这个调用。
  • 当你想在 DOM 树的不同部分渲染一段 JSX,而该部分不是你的组件的子节点(例如,一个模态框或一个提示框)时,使用 createPortal 而不是 createRoot

root.render(reactNode)

调用 root.render 将一段 JSX(“React 节点”)显示到 React 根的浏览器 DOM 节点中。

🌐 Call root.render to display a piece of JSX (“React node”) into the React root’s browser DOM node.

root.render(<App />);

React 会在 root 中显示 <App />,并接管其内部的 DOM 管理。

🌐 React will display <App /> in the root, and take over managing the DOM inside it.

查看更多示例。

参数

🌐 Parameters

  • reactNode:你想要显示的 React 节点。这通常是像 <App /> 这样的 JSX 片段,但你也可以传入使用 createElement() 构建的 React 元素、字符串、数字、nullundefined

返回

🌐 Returns

root.render 返回 undefined

注意事项

🌐 Caveats

  • 第一次调用 root.render 时,React 会在将 React 组件渲染到其中之前清除 React 根节点内的所有现有 HTML 内容。

  • 如果你的根 DOM 节点包含由 React 在服务器或构建期间生成的 HTML,请改用 hydrateRoot(),它会将事件处理程序附加到现有的 HTML 上。

  • 如果你在同一个根上多次调用 render,React 将根据需要更新 DOM,以反映你传入的最新 JSX。React 会通过”与之前渲染的树匹配”来决定 DOM 的哪些部分可以重用,哪些需要重新创建。在同一个根上再次调用 render 类似于在根组件上调用 set 函数:React 会避免不必要的 DOM 更新。

  • 虽然渲染一旦开始是同步的,root.render(...) 并不是。这意味着 root.render() 之后的代码可能会在该特定渲染的任何 effect(useLayoutEffectuseEffect)触发之前运行。通常这是没问题的,很少需要调整。在 effect 时间顺序很重要的少数情况下,你可以将 root.render(...) 封装在 flushSync 中,以确保初始渲染完全同步执行。

    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 根中的已渲染树。

🌐 Call root.unmount to destroy a rendered tree inside a React root.

root.unmount();

一个完全用 React 构建的应用通常不会有任何对 root.unmount 的调用。

🌐 An app fully built with React will usually not have any calls to root.unmount.

如果你的 React 根节点的 DOM 节点(或其任何祖级节点)可能会被其他代码从 DOM 中移除,这主要是有用的。例如,想象一个 jQuery 标签面板会从 DOM 中移除不活跃的标签。如果一个标签被移除,其内部的所有内容(包括内部的 React 根节点)也会从 DOM 中移除。在这种情况下,你需要通过调用 root.unmount 告诉 React 停止管理被移除根节点的内容。否则,被移除根节点内部的组件将不会知道去清理并释放全局资源,例如订阅。

🌐 This is mostly useful if your React root’s DOM node (or any of its ancestors) may get removed from the DOM by some other code. For example, imagine a jQuery tab panel that removes inactive tabs from the DOM. If a tab gets removed, everything inside it (including the React roots inside) would get removed from the DOM as well. In that case, you need to tell React to “stop” managing the removed root’s content by calling root.unmount. Otherwise, the components inside the removed root won’t know to clean up and free up global resources like subscriptions.

调用 root.unmount 会卸载根中的所有组件,并将 React 从根 DOM 节点中“分离”,包括移除树中的任何事件处理程序或状态。

🌐 Calling root.unmount will unmount all the components in the root and “detach” React from the root DOM node, including removing any event handlers or state in the tree.

参数

🌐 Parameters

root.unmount不接受任何参数。

返回

🌐 Returns

root.unmount 返回 undefined

注意事项

🌐 Caveats

  • 调用 root.unmount 会卸载树中的所有组件,并将 React 从根 DOM 节点“分离”。
  • 一旦你调用了 root.unmount,就不能在同一个根节点上再次调用 root.render。尝试在未挂载的根节点上调用 root.render 会抛出“无法更新未挂载的根节点”错误。然而,在该节点的前一个根节点已被卸载之后,你可以为同一个 DOM 节点创建一个新的根节点。

用法

🌐 Usage

渲染完全用 React 构建的应用

🌐 Rendering an app fully built with React

如果你的应用完全使用 React 构建,请为你的整个应用创建一个根目录。

🌐 If your app is fully built with React, create a single root for your entire app.

import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'));
root.render(<App />);

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

🌐 Usually, you only need to run this code once at startup. It will:

  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 时,每个组件都可以通过 使用 state 来实现。 当你需要在 DOM 节点外显示额外内容,例如模态框或提示框, 使用 portal 来渲染它。

🌐 From this point on, React will manage the DOM of your entire app. To add more components, nest them inside the App component. When you need to update the UI, each of your components can do this by using state. When you need to display extra content like a modal or a tooltip outside the DOM node, render it with a portal.

注意

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

🌐 When your HTML is empty, the user sees a blank page until the app’s JavaScript code loads and runs:

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

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

🌐 This can feel very slow! To solve this, you can generate the initial HTML from your components on the server or during the build. Then your visitors can read text, see images, and click links before any of the JavaScript code loads. We recommend using a framework that does this optimization out of the box. Depending on when it runs, this is called server-side rendering (SSR) or static site generation (SSG).

易犯错误

使用服务器渲染或静态生成的应用必须调用 hydrateRoot 而不是 createRoot 然后 React 会 hydrate(重用)你 HTML 中的 DOM 节点,而不是销毁并重新创建它们。


渲染部分使用 React 构建的页面

🌐 Rendering a page partially built with React

如果你的页面不是完全用 React 构建的,你可以多次调用 createRoot 为由 React 管理的每个顶层 UI 部分创建一个根。你可以通过调用 root.render 在每个根中显示不同的内容。

🌐 If your page isn’t fully built with React, you can call createRoot multiple times to create a root for each top-level piece of UI managed by React. You can display different content in each root by calling root.render.

在这里,两个不同的 React 组件被渲染到 index.html 文件中定义的两个 DOM 节点中:

🌐 Here, two different React components are rendered into two DOM nodes defined in the index.html file:

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 节点,并手动将其添加到文档中。

🌐 You could also create a new DOM node with document.createElement() and add it to the document manually.

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.

🌐 To remove the React tree from the DOM node and clean up all the resources used by it, call root.unmount.

root.unmount();

如果你的 React 组件位于用不同框架编写的应用中,这将非常有用。

🌐 This is mostly useful if your React components are inside an app written in a different framework.


更新根组件

🌐 Updating a root component

你可以在同一个根上多次调用 render。只要组件树结构与之前渲染的一致,React 就会保留状态. 注意你可以在输入框中输入,这意味着在这个示例中每秒重复的 render 调用的更新不会破坏原有内容:

🌐 You can call render more than once on the same root. As long as the component tree structure matches up with what was previously rendered, React will preserve the state. Notice how you can type in the input, which means that the updates from repeated render calls every second in this example are not destructive:

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 并不常见。通常,你的组件会改为更新状态

🌐 It is uncommon to call render multiple times. Usually, your components will update state instead.

生产中的错误日志记录

🌐 Error logging in production

默认情况下,React 会将所有错误记录到控制台。要实现你自己的错误报告,你可以提供可选的错误处理根选项 onUncaughtErroronCaughtErroronRecoverableError

🌐 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 and onRecoverableError:

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,
});
}
},
});

onCaughtError 选项是一个使用两个参数调用的函数:

  1. 抛出的 错误
  2. 一个 errorInfo 对象,包含错误的 componentStack

onUncaughtErroronRecoverableError 一起,你可以实现自己的错误报告系统:

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

故障排除

🌐 Troubleshooting

我已经创建了一个根,但没有显示任何内容

🌐 I’ve created a root, but nothing is displayed

确保你没有忘记将你的应用实际渲染到根目录中:

🌐 Make sure you haven’t forgotten to actually render your app into the root:

import { createRoot } from 'react-dom/client';
import App from './App.js';

const root = createRoot(document.getElementById('root'));
root.render(<App />);

在你这样做之前,不会显示任何内容。

🌐 Until you do that, nothing is displayed.


我收到一个错误:“你向 root.render 传递了第二个参数”

🌐 I’m getting an error: “You passed a second argument to root.render”

一个常见的错误是将 createRoot 的选项传递给 root.render(...)

🌐 A common mistake is to pass the options for createRoot to root.render(...):

Console

要修复,请将根选项传递给 createRoot(...),而不是 root.render(...)

🌐 To fix, pass the root options to createRoot(...), not 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 元素”

🌐 I’m getting an error: “Target container is not a DOM element”

此错误意味着你传递给 createRoot 的内容不是 DOM 节点。

🌐 This error means that whatever you’re passing to createRoot is not a DOM node.

如果你不确定发生了什么,请尝试记录它:

🌐 If you’re not sure what’s happening, try logging it:

const domNode = document.getElementById('root');
console.log(domNode); // ???
const root = createRoot(domNode);
root.render(<App />);

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

🌐 For example, if domNode is null, it means that getElementById returned null. This will happen if there is no node in the document with the given ID at the time of your call. There may be a few reasons for it:

  1. 你正在查找的 ID 可能与你在 HTML 文件中使用的 ID 不同。请检查是否有拼写错误!
  2. 你的包的 <script> 标签无法“看到” HTML 中出现在它之后的任何 DOM 节点。

另一个常见导致此错误的方法是写成 createRoot(<App />) 而不是 createRoot(domNode)


我收到一个错误:“函数不能作为 React 子元素。”

🌐 I’m getting an error: “Functions are not valid as a React child.”

此错误意味着你传递给 root.render 的内容不是一个 React 组件。

🌐 This error means that whatever you’re passing to root.render is not a React component.

如果你调用 root.render 时使用 Component 而不是 <Component />,可能会发生这种情况:

🌐 This may happen if you call root.render with Component instead of <Component />:

// 🚩 Wrong: App is a function, not a Component.
root.render(App);

// ✅ Correct: <App /> is a component.
root.render(<App />);

或者如果你把一个函数传给 root.render,而不是把调用它的结果传给它:

🌐 Or if you pass a function to root.render, instead of the result of calling it:

// 🚩 Wrong: createApp is a function, not a component.
root.render(createApp);

// ✅ Correct: call createApp to return a component.
root.render(createApp());

我的服务器渲染的 HTML 从头开始重新创建

🌐 My server-rendered HTML gets re-created from scratch

如果你的应用是服务器渲染的,并且包含由 React 生成的初始 HTML,你可能会注意到创建一个根并调用 root.render 会删除所有这些 HTML,然后从头重新创建所有 DOM 节点。这可能更慢,会重置焦点和滚动位置,并可能丢失其他用户输入。

🌐 If your app is server-rendered and includes the initial HTML generated by React, you might notice that creating a root and calling root.render deletes all that HTML, and then re-creates all the DOM nodes from scratch. This can be slower, resets focus and scroll positions, and may lose other user input.

服务器渲染的应用必须使用 hydrateRoot 而不是 createRoot

🌐 Server-rendered apps must use hydrateRoot instead of createRoot:

import { hydrateRoot } from 'react-dom/client';
import App from './App.js';

hydrateRoot(
document.getElementById('root'),
<App />
);

请注意,它的 API 是不同的。尤其是,通常不会有进一步的 root.render 调用。

🌐 Note that its API is different. In particular, usually there will be no further root.render call.