createRoot cho phép bạn tạo một gốc để hiển thị các thành phần React bên trong một nút DOM của trình duyệt.

const root = createRoot(domNode, options?)

Tham khảo

createRoot(domNode, options?)

Gọi createRoot để tạo một gốc React để hiển thị nội dung bên trong một phần tử DOM của trình duyệt.

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

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

React sẽ tạo một gốc cho domNode và tiếp quản việc quản lý DOM bên trong nó. Sau khi bạn đã tạo một gốc, bạn cần gọi root.render để hiển thị một thành phần React bên trong nó:

root.render(<App />);

Một ứng dụng được xây dựng hoàn toàn bằng React thường chỉ có một lệnh gọi createRoot cho thành phần gốc của nó. Một trang sử dụng “rắc” React cho các phần của trang có thể có nhiều gốc riêng biệt khi cần thiết.

Xem thêm các ví dụ bên dưới.

Tham số

  • domNode: Một phần tử DOM. React sẽ tạo một gốc cho phần tử DOM này và cho phép bạn gọi các hàm trên gốc, chẳng hạn như render để hiển thị nội dung React đã được render.

  • tùy chọn options: Một đối tượng với các tùy chọn cho gốc React này.

    • tùy chọn onCaughtError: Callback được gọi khi React bắt được lỗi trong một Error Boundary. Được gọi với error bị bắt bởi Error Boundary và một đối tượng errorInfo chứa componentStack.
    • tùy chọn onUncaughtError: Callback được gọi khi một lỗi được ném ra và không bị bắt bởi một Error Boundary. Được gọi với error đã được ném ra và một đối tượng errorInfo chứa componentStack.
    • tùy chọn onRecoverableError: Callback được gọi khi React tự động phục hồi từ các lỗi. Được gọi với một error mà React ném ra và một đối tượng errorInfo chứa componentStack. Một số lỗi có thể phục hồi có thể bao gồm nguyên nhân gây ra lỗi ban đầu là error.cause.
    • tùy chọn identifierPrefix: Một tiền tố chuỗi mà React sử dụng cho các ID được tạo bởi useId. Hữu ích để tránh xung đột khi sử dụng nhiều gốc trên cùng một trang.

Trả về

createRoot trả về một đối tượng với hai phương thức: renderunmount.

Lưu ý

  • Nếu ứng dụng của bạn được render phía máy chủ, việc sử dụng createRoot() không được hỗ trợ. Sử dụng hydrateRoot() thay thế.
  • Bạn có thể chỉ có một lệnh gọi createRoot trong ứng dụng của mình. Nếu bạn sử dụng một framework, nó có thể thực hiện lệnh gọi này cho bạn.
  • Khi bạn muốn render một đoạn JSX ở một phần khác của cây DOM mà không phải là con của thành phần của bạn (ví dụ: một modal hoặc một tooltip), hãy sử dụng createPortal thay vì createRoot.

root.render(reactNode)

Gọi root.render để hiển thị một đoạn JSX (“React node”) vào nút DOM của trình duyệt của gốc React.

root.render(<App />);

React sẽ hiển thị <App /> trong root và tiếp quản việc quản lý DOM bên trong nó.

Xem thêm các ví dụ bên dưới.

Tham số

  • reactNode: Một React node mà bạn muốn hiển thị. Đây thường sẽ là một đoạn JSX như <App />, nhưng bạn cũng có thể truyền một phần tử React được xây dựng bằng createElement(), một chuỗi, một số, null hoặc undefined.

Trả về

root.render trả về undefined.

Lưu ý

  • Lần đầu tiên bạn gọi root.render, React sẽ xóa tất cả nội dung HTML hiện có bên trong gốc React trước khi render thành phần React vào đó.

  • Nếu nút DOM của gốc của bạn chứa HTML được tạo bởi React trên máy chủ hoặc trong quá trình xây dựng, hãy sử dụng hydrateRoot() thay thế, nó sẽ đính kèm các trình xử lý sự kiện vào HTML hiện có.

  • Nếu bạn gọi render trên cùng một gốc nhiều lần, React sẽ cập nhật DOM khi cần thiết để phản ánh JSX mới nhất mà bạn đã truyền. React sẽ quyết định phần nào của DOM có thể được sử dụng lại và phần nào cần được tạo lại bằng cách “ghép nó lại” với cây đã được render trước đó. Gọi render trên cùng một gốc một lần nữa tương tự như gọi hàm set trên thành phần gốc: React tránh các cập nhật DOM không cần thiết.

  • Mặc dù việc render là đồng bộ sau khi nó bắt đầu, root.render(...) thì không. Điều này có nghĩa là mã sau root.render() có thể chạy trước bất kỳ hiệu ứng nào (useLayoutEffect, useEffect) của quá trình render cụ thể đó được kích hoạt. Điều này thường ổn và hiếm khi cần điều chỉnh. Trong những trường hợp hiếm hoi mà thời gian hiệu ứng quan trọng, bạn có thể bọc root.render(...) trong flushSync để đảm bảo quá trình render ban đầu chạy hoàn toàn đồng bộ.

    const root = createRoot(document.getElementById('root'));
    root.render(<App />);
    // 🚩 HTML sẽ không bao gồm <App /> đã được render:
    console.log(document.body.innerHTML);

root.unmount()

Gọi root.unmount để phá hủy một cây đã được render bên trong một gốc React.

root.unmount();

Một ứng dụng được xây dựng hoàn toàn bằng React thường sẽ không có bất kỳ lệnh gọi nào đến root.unmount.

Điều này chủ yếu hữu ích nếu nút DOM của gốc React của bạn (hoặc bất kỳ tổ tiên nào của nó) có thể bị xóa khỏi DOM bởi một số mã khác. Ví dụ: hãy tưởng tượng một bảng điều khiển tab jQuery loại bỏ các tab không hoạt động khỏi DOM. Nếu một tab bị xóa, mọi thứ bên trong nó (bao gồm cả các gốc React bên trong) cũng sẽ bị xóa khỏi DOM. Trong trường hợp đó, bạn cần báo cho React “dừng” quản lý nội dung của gốc đã bị xóa bằng cách gọi root.unmount. Nếu không, các thành phần bên trong gốc đã bị xóa sẽ không biết cách dọn dẹp và giải phóng các tài nguyên toàn cục như đăng ký.

Gọi root.unmount sẽ unmount tất cả các thành phần trong gốc và “tách” React khỏi nút DOM gốc, bao gồm cả việc xóa bất kỳ trình xử lý sự kiện hoặc trạng thái nào trong cây.

Tham số

root.unmount không chấp nhận bất kỳ tham số nào.

Trả về

root.unmount trả về undefined.

Lưu ý

  • Gọi root.unmount sẽ unmount tất cả các thành phần trong cây và “tách” React khỏi nút DOM gốc.

  • Sau khi bạn gọi root.unmount, bạn không thể gọi lại root.render trên cùng một gốc. Cố gắng gọi root.render trên một gốc đã unmount sẽ ném ra lỗi “Không thể cập nhật một gốc đã unmount”. Tuy nhiên, bạn có thể tạo một gốc mới cho cùng một nút DOM sau khi gốc trước đó cho nút đó đã được unmount.


Cách sử dụng

Render một ứng dụng được xây dựng hoàn toàn bằng React

Nếu ứng dụng của bạn được xây dựng hoàn toàn bằng React, hãy tạo một gốc duy nhất cho toàn bộ ứng dụng của bạn.

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

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

Thông thường, bạn chỉ cần chạy mã này một lần khi khởi động. Nó sẽ:

  1. Tìm nút DOM của trình duyệt được xác định trong HTML của bạn.
  2. Hiển thị thành phần React cho ứng dụng của bạn bên trong.
import { createRoot } from 'react-dom/client';
import App from './App.js';
import './styles.css';

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

Nếu ứng dụng của bạn được xây dựng hoàn toàn bằng React, bạn không cần tạo thêm bất kỳ gốc nào hoặc gọi lại root.render.

Từ thời điểm này trở đi, React sẽ quản lý DOM của toàn bộ ứng dụng của bạn. Để thêm nhiều thành phần hơn, lồng chúng bên trong thành phần App. Khi bạn cần cập nhật UI, mỗi thành phần của bạn có thể thực hiện việc này bằng cách sử dụng state. Khi bạn cần hiển thị nội dung bổ sung như modal hoặc tooltip bên ngoài nút DOM, render nó bằng một portal.

Note

Khi HTML của bạn trống, người dùng sẽ thấy một trang trống cho đến khi mã JavaScript của ứng dụng tải và chạy:

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

Điều này có thể cảm thấy rất chậm! Để giải quyết vấn đề này, bạn có thể tạo HTML ban đầu từ các thành phần của mình trên máy chủ hoặc trong quá trình xây dựng. Sau đó, khách truy cập của bạn có thể đọc văn bản, xem hình ảnh và nhấp vào liên kết trước khi bất kỳ mã JavaScript nào tải. Chúng tôi khuyên bạn nên sử dụng một framework thực hiện tối ưu hóa này ngay lập tức. Tùy thuộc vào thời điểm nó chạy, điều này được gọi là server-side rendering (SSR) hoặc static site generation (SSG).

Chú Ý

Các ứng dụng sử dụng server rendering hoặc static generation phải gọi hydrateRoot thay vì createRoot. React sau đó sẽ hydrate (sử dụng lại) các nút DOM từ HTML của bạn thay vì phá hủy và tạo lại chúng.


Render một trang được xây dựng một phần bằng React

Nếu trang của bạn không được xây dựng hoàn toàn bằng React, bạn có thể gọi createRoot nhiều lần để tạo một gốc cho mỗi phần UI cấp cao nhất được quản lý bởi React. Bạn có thể hiển thị nội dung khác nhau trong mỗi gốc bằng cách gọi root.render.

Ở đây, hai thành phần React khác nhau được render vào hai nút DOM được xác định trong tệp index.html:

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

Bạn cũng có thể tạo một nút DOM mới bằng document.createElement() và thêm nó vào tài liệu theo cách thủ công.

const domNode = document.createElement('div');
const root = createRoot(domNode);
root.render(<Comment />);
document.body.appendChild(domNode); // Bạn có thể thêm nó vào bất kỳ đâu trong tài liệu

Để xóa cây React khỏi nút DOM và dọn dẹp tất cả các tài nguyên được sử dụng bởi nó, hãy gọi root.unmount.

root.unmount();

Điều này chủ yếu hữu ích nếu các thành phần React của bạn nằm bên trong một ứng dụng được viết bằng một framework khác.


Cập nhật một thành phần gốc

Bạn có thể gọi render nhiều lần trên cùng một gốc. Miễn là cấu trúc cây thành phần khớp với những gì đã được render trước đó, React sẽ giữ lại trạng thái. Lưu ý cách bạn có thể nhập vào đầu vào, điều đó có nghĩa là các bản cập nhật từ các lệnh gọi render lặp đi lặp lại mỗi giây trong ví dụ này không mang tính phá hủy:

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

Việc gọi render nhiều lần là không phổ biến. Thông thường, các thành phần của bạn sẽ cập nhật state thay thế.

Ghi nhật ký lỗi trong sản xuất

Theo mặc định, React sẽ ghi tất cả các lỗi vào bảng điều khiển. Để triển khai báo cáo lỗi của riêng bạn, bạn có thể cung cấp các tùy chọn gốc trình xử lý lỗi tùy chọn onUncaughtError, onCaughtErroronRecoverableError:

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

Tùy chọn onCaughtError là một hàm được gọi với hai đối số:

  1. lỗi đã được ném ra.
  2. Một đối tượng errorInfo chứa componentStack của lỗi.

Cùng với onUncaughtErroronRecoverableError, bạn có thể triển khai hệ thống báo cáo lỗi của riêng mình:

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, {
  // Hãy nhớ xóa các tùy chọn này trong quá trình phát triển để tận dụng
  // các trình xử lý mặc định của React hoặc triển khai lớp phủ của riêng bạn để phát triển.
  // Các trình xử lý chỉ được chỉ định vô điều kiện ở đây cho mục đích trình diễn.
  onCaughtError: onCaughtErrorProd,
  onRecoverableError: onRecoverableErrorProd,
  onUncaughtError: onUncaughtErrorProd,
});
root.render(<App />);

Khắc phục sự cố

Tôi đã tạo một gốc, nhưng không có gì được hiển thị

Đảm bảo rằng bạn không quên thực sự render ứng dụng của mình vào gốc:

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

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

Cho đến khi bạn làm điều đó, không có gì được hiển thị.


Tôi gặp lỗi: “Bạn đã truyền một đối số thứ hai cho root.render”

Một lỗi phổ biến là truyền các tùy chọn cho createRoot cho root.render(...):

Console
Cảnh báo: Bạn đã truyền một đối số thứ hai cho root.render(…) nhưng nó chỉ chấp nhận một đối số.

Để sửa lỗi, hãy truyền các tùy chọn gốc cho createRoot(...), không phải root.render(...):

// 🚩 Sai: root.render chỉ nhận một đối số.
root.render(App, {onUncaughtError});

// ✅ Đúng: truyền các tùy chọn cho createRoot.
const root = createRoot(container, {onUncaughtError});
root.render(<App />);

Tôi gặp lỗi: “Target container is not a DOM element”

Lỗi này có nghĩa là bất cứ thứ gì bạn đang truyền cho createRoot không phải là một nút DOM.

Nếu bạn không chắc chắn điều gì đang xảy ra, hãy thử ghi nhật ký nó:

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

Ví dụ: nếu domNodenull, điều đó có nghĩa là getElementById trả về null. Điều này sẽ xảy ra nếu không có nút nào trong tài liệu có ID đã cho tại thời điểm bạn gọi. Có thể có một vài lý do cho việc này:

  1. ID bạn đang tìm kiếm có thể khác với ID bạn đã sử dụng trong tệp HTML. Kiểm tra lỗi chính tả!
  2. Thẻ <script> của bundle của bạn không thể “nhìn thấy” bất kỳ nút DOM nào xuất hiện sau nó trong HTML.

Một cách phổ biến khác để gặp lỗi này là viết createRoot(<App />) thay vì createRoot(domNode).


Tôi gặp lỗi: “Functions are not valid as a React child.”

Lỗi này có nghĩa là bất cứ thứ gì bạn đang truyền cho root.render không phải là một thành phần React.

Điều này có thể xảy ra nếu bạn gọi root.render với Component thay vì <Component />:

// 🚩 Sai: App là một hàm, không phải một thành phần.
root.render(App);

// ✅ Đúng: <App /> là một thành phần.
root.render(<App />);

Hoặc nếu bạn truyền một hàm cho root.render, thay vì kết quả của việc gọi nó:

// 🚩 Sai: createApp là một hàm, không phải một thành phần.
root.render(createApp);

// ✅ Đúng: gọi createApp để trả về một thành phần.
root.render(createApp());

HTML được render phía máy chủ của tôi được tạo lại từ đầu

Nếu ứng dụng của bạn được render phía máy chủ và bao gồm HTML ban đầu được tạo bởi React, bạn có thể nhận thấy rằng việc tạo một gốc và gọi root.render sẽ xóa tất cả HTML đó và sau đó tạo lại tất cả các nút DOM từ đầu. Điều này có thể chậm hơn, đặt lại vị trí tiêu điểm và cuộn và có thể làm mất các đầu vào khác của người dùng.

Các ứng dụng được render phía máy chủ phải sử dụng hydrateRoot thay vì createRoot:

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

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

Lưu ý rằng API của nó khác. Đặc biệt, thường sẽ không có lệnh gọi root.render nào nữa.