createPortal cho phép bạn hiển thị một số phần tử con vào một phần khác của DOM.

<div>
<SomeComponent />
{createPortal(children, domNode, key?)}
</div>

Tham khảo

createPortal(children, domNode, key?)

Để tạo một portal, hãy gọi createPortal, truyền một số JSX và DOM node nơi nó sẽ được hiển thị:

import { createPortal } from 'react-dom';

// ...

<div>
<p>Phần tử con này được đặt trong div cha.</p>
{createPortal(
<p>Phần tử con này được đặt trong document body.</p>,
document.body
)}
</div>

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

Một portal chỉ thay đổi vị trí vật lý của DOM node. Về mọi mặt khác, JSX bạn hiển thị vào một portal hoạt động như một node con của React component hiển thị nó. Ví dụ: phần tử con có thể truy cập context được cung cấp bởi cây cha và các event nổi lên từ con đến cha theo cây React.

Tham số

  • children: Bất cứ thứ gì có thể được hiển thị bằng React, chẳng hạn như một đoạn JSX (ví dụ: <div /> hoặc <SomeComponent />), một Fragment (<>...</>), một chuỗi hoặc một số, hoặc một mảng các phần tử này.

  • domNode: Một số DOM node, chẳng hạn như những node được trả về bởi document.getElementById(). Node này phải đã tồn tại. Việc truyền một DOM node khác trong quá trình cập nhật sẽ khiến nội dung portal được tạo lại.

  • optional key: Một chuỗi hoặc số duy nhất được sử dụng làm key của portal.

Giá trị trả về

createPortal trả về một React node có thể được đưa vào JSX hoặc trả về từ một React component. Nếu React bắt gặp nó trong đầu ra hiển thị, nó sẽ đặt children đã cung cấp bên trong domNode đã cung cấp.

Lưu ý

  • Các event từ portal lan truyền theo cây React chứ không phải cây DOM. Ví dụ: nếu bạn nhấp vào bên trong một portal và portal được bao bọc trong <div onClick>, thì trình xử lý onClick đó sẽ kích hoạt. Nếu điều này gây ra sự cố, hãy dừng sự lan truyền event từ bên trong portal hoặc di chuyển chính portal lên trên trong cây React.

Cách sử dụng

Hiển thị đến một phần khác của DOM

Portals cho phép các component của bạn hiển thị một số phần tử con của chúng vào một vị trí khác trong DOM. Điều này cho phép một phần của component của bạn “thoát” khỏi bất kỳ container nào mà nó có thể ở trong đó. Ví dụ: một component có thể hiển thị một hộp thoại modal hoặc một tooltip xuất hiện phía trên và bên ngoài phần còn lại của trang.

Để tạo một portal, hãy hiển thị kết quả của createPortal với một số JSXDOM node nơi nó sẽ đi đến:

import { createPortal } from 'react-dom'; function MyComponent() { return ( <div style={{ border: '2px solid black' }}> <p>Phần tử con này được đặt trong div cha.</p> {createPortal( <p>Phần tử con này được đặt trong document body.</p>, document.body )} </div> ); }

React sẽ đặt các DOM node cho JSX bạn đã truyền vào bên trong DOM node bạn đã cung cấp.

Nếu không có portal, <p> thứ hai sẽ được đặt bên trong <div> cha, nhưng portal đã “dịch chuyển” nó vào document.body:

import { createPortal } from 'react-dom';

export default function MyComponent() {
  return (
    <div style={{ border: '2px solid black' }}>
      <p>Phần tử con này được đặt trong div cha.</p>
      {createPortal(
        <p>Phần tử con này được đặt trong document body.</p>,
        document.body
      )}
    </div>
  );
}

Lưu ý cách đoạn văn thứ hai xuất hiện trực quan bên ngoài <div> cha có đường viền. Nếu bạn kiểm tra cấu trúc DOM bằng các công cụ dành cho nhà phát triển, bạn sẽ thấy rằng <p> thứ hai đã được đặt trực tiếp vào <body>:

<body>
<div id="root">
...
<div style="border: 2px solid black">
<p>Phần tử con này được đặt bên trong div cha.</p>
</div>
...
</div>
<p>Phần tử con này được đặt trong document body.</p>
</body>

Một portal chỉ thay đổi vị trí vật lý của DOM node. Về mọi mặt khác, JSX bạn hiển thị vào một portal hoạt động như một node con của React component hiển thị nó. Ví dụ: phần tử con có thể truy cập context được cung cấp bởi cây cha và các event vẫn nổi lên từ con đến cha theo cây React.


Hiển thị một hộp thoại modal bằng portal

Bạn có thể sử dụng một portal để tạo một hộp thoại modal nổi phía trên phần còn lại của trang, ngay cả khi component triệu hồi hộp thoại nằm bên trong một container có overflow: hidden hoặc các style khác gây trở ngại cho hộp thoại.

Trong ví dụ này, hai container có các style làm gián đoạn hộp thoại modal, nhưng hộp thoại được hiển thị vào một portal không bị ảnh hưởng vì, trong DOM, modal không nằm trong các phần tử JSX cha.

import NoPortalExample from './NoPortalExample';
import PortalExample from './PortalExample';

export default function App() {
  return (
    <>
      <div className="clipping-container">
        <NoPortalExample  />
      </div>
      <div className="clipping-container">
        <PortalExample />
      </div>
    </>
  );
}

Chú Ý

Điều quan trọng là đảm bảo rằng ứng dụng của bạn có thể truy cập được khi sử dụng portal. Ví dụ: bạn có thể cần quản lý focus bàn phím để người dùng có thể di chuyển focus vào và ra khỏi portal một cách tự nhiên.

Hãy tuân theo WAI-ARIA Modal Authoring Practices khi tạo modal. Nếu bạn sử dụng một gói cộng đồng, hãy đảm bảo rằng nó có thể truy cập được và tuân theo các nguyên tắc này.


Hiển thị các React component vào markup server không phải React

Portal có thể hữu ích nếu React root của bạn chỉ là một phần của một trang tĩnh hoặc được hiển thị trên server mà không được xây dựng bằng React. Ví dụ: nếu trang của bạn được xây dựng bằng một framework server như Rails, bạn có thể tạo các khu vực tương tác trong các khu vực tĩnh như sidebar. So với việc có nhiều React root riêng biệt, portal cho phép bạn coi ứng dụng như một cây React duy nhất với trạng thái được chia sẻ ngay cả khi các phần của nó hiển thị đến các phần khác nhau của DOM.

import { createPortal } from 'react-dom';

const sidebarContentEl = document.getElementById('sidebar-content');

export default function App() {
  return (
    <>
      <MainContent />
      {createPortal(
        <SidebarContent />,
        sidebarContentEl
      )}
    </>
  );
}

function MainContent() {
  return <p>Phần này được hiển thị bởi React</p>;
}

function SidebarContent() {
  return <p>Phần này cũng được hiển thị bởi React!</p>;
}


Hiển thị các React component vào các DOM node không phải React

Bạn cũng có thể sử dụng một portal để quản lý nội dung của một DOM node được quản lý bên ngoài React. Ví dụ: giả sử bạn đang tích hợp với một widget bản đồ không phải React và bạn muốn hiển thị nội dung React bên trong một popup. Để thực hiện việc này, hãy khai báo một biến trạng thái popupContainer để lưu trữ DOM node mà bạn sẽ hiển thị vào:

const [popupContainer, setPopupContainer] = useState(null);

Khi bạn tạo widget của bên thứ ba, hãy lưu trữ DOM node được trả về bởi widget để bạn có thể hiển thị vào nó:

useEffect(() => {
if (mapRef.current === null) {
const map = createMapWidget(containerRef.current);
mapRef.current = map;
const popupDiv = addPopupToMapWidget(map);
setPopupContainer(popupDiv);
}
}, []);

Điều này cho phép bạn sử dụng createPortal để hiển thị nội dung React vào popupContainer sau khi nó khả dụng:

return (
<div style={{ width: 250, height: 250 }} ref={containerRef}>
{popupContainer !== null && createPortal(
<p>Xin chào từ React!</p>,
popupContainer
)}
</div>
);

Dưới đây là một ví dụ hoàn chỉnh mà bạn có thể dùng thử:

import { useRef, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { createMapWidget, addPopupToMapWidget } from './map-widget.js';

export default function Map() {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const [popupContainer, setPopupContainer] = useState(null);

  useEffect(() => {
    if (mapRef.current === null) {
      const map = createMapWidget(containerRef.current);
      mapRef.current = map;
      const popupDiv = addPopupToMapWidget(map);
      setPopupContainer(popupDiv);
    }
  }, []);

  return (
    <div style={{ width: 250, height: 250 }} ref={containerRef}>
      {popupContainer !== null && createPortal(
        <p>Xin chào từ React!</p>,
        popupContainer
      )}
    </div>
  );
}