createPortal
createPortal позволяет отобразить некоторые дочерние элементы в другой части DOM.
<div>
<SomeComponent />
{createPortal(children, domNode, key?)}
</div>
Ссылка
createPortal(children, domNode, key?)
Чтобы создать портал, вызовите createPortal, передав JSX и узел DOM, в который он должен быть отображён:
import { createPortal } from 'react-dom';
// ...
<div>
<p>This child is placed in the parent div.</p>
{createPortal(
<p>This child is placed in the document body.</p>,
document.body
)}
</div> См. дополнительные примеры ниже.
Портал только изменяет физическое расположение узла DOM. Во всех остальных отношениях JSX, который вы отображаете в портале, действует как дочерний узел компонента React, который его отображает. Например, дочерний элемент может получить доступ к контексту, предоставляемому родительским деревом, и события фильтруются от дочерних элементов к родительским в соответствии с деревом React.
Параметры
-
children: Любой элемент, который можно отобразить с помощью React, такой как фрагмент JSX (например,<div />или<SomeComponent />), Fragment (<>...</>), строка, число или массив этих элементов. -
domNode: Узел DOM, например, возвращаемыйdocument.getElementById(). Узел должен уже существовать. Передача другого узла DOM во время обновления приведет к повторному созданию содержимого портала. -
необязательно
key: Уникальная строка или число, используемая в качестве ключа портала. Ключ.
Возвращаемое значение
createPortal возвращает узел React, который можно включить в JSX или вернуть из компонента React. Если React обнаружит его в выходных данных рендеринга, он поместит предоставленный children внутрь предоставленного domNode.
Ограничения
- События из порталов распространяются в соответствии с деревом React, а не деревом DOM. Например, если вы нажмёте внутри портала, и портал обернут в
<div onClick>, этот обработчикonClickбудет вызван. Если это вызывает проблемы, либо остановите распространение события внутри портала, либо переместите сам портал выше в дереве React.
Использование
Отображение в другой части DOM
Порталы позволяют вашим компонентам отображать некоторые из их дочерних элементов в другом месте DOM. Это позволяет части вашего компонента «вырваться» из любых контейнеров, в которых она может находиться. Например, компонент может отображать модальное диалоговое окно или всплывающую подсказку, которая появляется над и вне остальной частью страницы.
Чтобы создать портал, отобразите результат createPortal с некоторым JSX и узлом DOM, в который он должен быть помещён:
import { createPortal } from 'react-dom';
function MyComponent() {
return (
<div style={{ border: '2px solid black' }}>
<p>This child is placed in the parent div.</p>
{createPortal(
<p>This child is placed in the document body.</p>,
document.body
)}
</div>
);
} React поместит узлы DOM для переданного вами JSX внутрь узла DOM, который вы предоставили.
Без портала второй <p> был бы размещён внутри родительского <div>, но портал «перенес» его в document.body:
import { createPortal } from 'react-dom'; export default function MyComponent() { return ( <div style={{ border: '2px solid black' }}> <p>This child is placed in the parent div.</p> {createPortal( <p>This child is placed in the document body.</p>, document.body )} </div> ); }
Обратите внимание, как второй абзац визуально появляется вне родительского <div> с рамкой. Если вы просмотрите структуру DOM с помощью инструментов разработчика, вы увидите, что второй <p> был помещён непосредственно в <body>:
<body>
<div id="root">
...
<div style="border: 2px solid black">
<p>This child is placed inside the parent div.</p>
</div>
...
</div>
<p>This child is placed in the document body.</p>
</body> Портал только изменяет физическое расположение узла DOM. Во всех остальных отношениях JSX, который вы отображаете в портале, действует как дочерний узел компонента React, который его отображает. Например, дочерний элемент может получить доступ к контексту, предоставляемому родительским деревом, и события всё ещё фильтруются от дочерних элементов к родительским в соответствии с деревом React.
Отображение модального диалогового окна с помощью портала
Вы можете использовать портал для создания модального диалогового окна, которое плавает над остальной частью страницы, даже если компонент, вызывающий диалоговое окно, находится внутри контейнера с overflow: hidden или другими стилями, которые мешают диалоговому окну.
В этом примере два контейнера имеют стили, которые нарушают работу модального диалогового окна, но диалоговое окно, отображаемое в портал, не затрагивается, поскольку в DOM модальное окно не содержится в родительских элементах JSX.
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> </> ); }
Возможные проблемы
Важно убедиться, что ваш интерфейс доступен при использовании порталов. Например, вам может потребоваться управлять фокусом клавиатуры, чтобы пользователь мог перемещать фокус в портал и из него естественным образом.
Следуйте Рекомендациям по разработке модальных диалогов WAI-ARIA при создании модальных окон. Если вы используете стороннюю библиотеку, убедитесь, что она доступна и соответствует этим рекомендациям.
Отображение компонентов React в разметке сервера, не использующей React
Порталы могут быть полезны, если корень вашего React приложения является только частью статической или рендерящейся на сервере страницы, которая не построена с помощью React. Например, если ваша страница построена с помощью фреймворка сервера, такого как Rails, вы можете создать области интерактивности внутри статических областей, таких как сайдбары. По сравнению с использованием нескольких отдельных корней React, порталы позволяют вам рассматривать приложение как одно дерево React с общим состоянием, даже если его части отображаются в разных частях 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>; }
Отображение компонентов React в узлах DOM, не использующих React
Вы также можете использовать портал для управления содержимым узла DOM, который управляется вне React. Например, предположим, что вы интегрируетесь с виджетом карты, не использующим React, и хотите отобразить содержимое React внутри всплывающей подсказки. Для этого объявите переменную состояния popupContainer для хранения узла DOM, в который вы собираетесь отображать:
const [popupContainer, setPopupContainer] = useState(null); При создании стороннего виджета сохраните узел DOM, возвращаемый виджетом, чтобы вы могли отображать в него:
useEffect(() => {
if (mapRef.current === null) {
const map = createMapWidget(containerRef.current);
mapRef.current = map;
const popupDiv = addPopupToMapWidget(map);
setPopupContainer(popupDiv);
}
}, []); Это позволяет использовать createPortal для отображения содержимого React в popupContainer после его доступности:
return (
<div style={{ width: 250, height: 250 }} ref={containerRef}>
{popupContainer !== null && createPortal(
<p>Hello from React!</p>,
popupContainer
)}
</div>
); Вот полный пример, с которым можно поработать:
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> ); }
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://18.react.dev/reference/react-dom/createPortal