createPortal
createPortal
cho phép bạn kết xuất một số children 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à nút DOM nơi nó sẽ được kết xuất:
import { createPortal } from 'react-dom';
// ...
<div>
<p>Child này được đặt trong div cha.</p>
{createPortal(
<p>Child này được đặt trong phần body của tài liệu.</p>,
document.body
)}
</div>
Một portal chỉ thay đổi vị trí vật lý của nút DOM. Về mọi mặt khác, JSX bạn kết xuất vào một portal hoạt động như một nút con của thành phần React kết xuất nó. Ví dụ: child có thể truy cập ngữ cảnh được cung cấp bởi cây cha và các sự kiện nổi lên từ children lên cha theo cây React.
Tham số
-
children
: Bất cứ thứ gì có thể được kết xuất 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ố nút DOM, chẳng hạn như những nút được trả về bởidocument.getElementById()
. Nút phải đã tồn tại. Truyền một nút DOM khác trong quá trình cập nhật sẽ khiến nội dung cổng được tạo lại. -
tùy chọn
key
: Một chuỗi hoặc số duy nhất được sử dụng làm key của portal.
Trả về
createPortal
trả về một nút React có thể được đưa vào JSX hoặc được trả về từ một thành phần React. Nếu React gặp nó trong đầu ra kết xuất, nó sẽ đặt children
đã cung cấp bên trong domNode
đã cung cấp.
Lưu ý
- Các sự kiện từ cổng 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 cổng và cổng được bao bọc trong
<div onClick>
, 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 lan truyền sự kiện từ bên trong cổng hoặc di chuyển chính cổng đó lên trên cây React.
Cách sử dụng
Kết xuất đến một phần khác của DOM
Portals cho phép các thành phần của bạn kết xuất một số children 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 thành phần của bạn “thoát” khỏi bất kỳ vùng chứa nào mà nó có thể ở trong đó. Ví dụ: một thành phần có thể hiển thị một hộp thoại phương thức hoặc một chú giải công cụ 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 kết xuất kết quả của createPortal
với một số JSX và nút DOM nơi nó sẽ đi:
import { createPortal } from 'react-dom';
function MyComponent() {
return (
<div style={{ border: '2px solid black' }}>
<p>Child này được đặt trong div cha.</p>
{createPortal(
<p>Child này được đặt trong phần body của tài liệu.</p>,
document.body
)}
</div>
);
}
React sẽ đặt các nút DOM cho JSX bạn đã truyền bên trong nút DOM 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>Child này được đặt trong div cha.</p> {createPortal( <p>Child này được đặt trong phần body của tài liệu.</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>Child này được đặt bên trong div cha.</p>
</div>
...
</div>
<p>Child này được đặt trong phần body của tài liệu.</p>
</body>
Một portal chỉ thay đổi vị trí vật lý của nút DOM. Về mọi mặt khác, JSX bạn kết xuất vào một portal hoạt động như một nút con của thành phần React kết xuất nó. Ví dụ: child có thể truy cập ngữ cảnh được cung cấp bởi cây cha và các sự kiện vẫn nổi lên từ children lên cha theo cây React.
Kết xuất hộp thoại phương thức bằng portal
Bạn có thể sử dụng một portal để tạo một hộp thoại phương thức nổi phía trên phần còn lại của trang, ngay cả khi thành phần triệu hồi hộp thoại nằm bên trong một vùng chứa có overflow: hidden
hoặc các kiểu khác gây trở ngại cho hộp thoại.
Trong ví dụ này, hai vùng chứa có các kiểu làm gián đoạn hộp thoại phương thức, nhưng vùng chứa được kết xuất vào một portal không bị ảnh hưởng vì, trong DOM, phương thức 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> </> ); }
Kết xuất các thành phần React vào mã đánh dấu máy chủ không phải React
Portals có thể hữu ích nếu gốc React của bạn chỉ là một phần của trang tĩnh hoặc được kết xuất trên máy chủ 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 máy chủ 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ư thanh bên. So với việc có nhiều gốc React riêng biệt, portals 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ó kết xuất đế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>This part is rendered by React</p>; } function SidebarContent() { return <p>This part is also rendered by React!</p>; }
Kết xuất các thành phần React vào các nút DOM không phải React
Bạn cũng có thể sử dụng cổng để quản lý nội dung của một nút DOM được quản lý bên ngoài React. Ví dụ: giả sử bạn đang tích hợp với một tiện ích bản đồ không phải React và bạn muốn kết xuất nội dung React bên trong một cửa sổ bật lên. Để 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ữ nút DOM mà bạn sẽ kết xuất vào:
const [popupContainer, setPopupContainer] = useState(null);
Khi bạn tạo tiện ích của bên thứ ba, hãy lưu trữ nút DOM được trả về bởi tiện ích để bạn có thể kết xuất 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
để kết xuất 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>Hello from React!</p>,
popupContainer
)}
</div>
);
Dưới đây là một ví dụ hoàn chỉnh mà bạn có thể 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>Hello from React!</p>, popupContainer )} </div> ); }