createPortal
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>
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ởidocument.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ố JSX và DOM 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> </> ); }
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> ); }