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> </> ); }
Опасность
Важно убедиться, что ваше приложение доступно при использовании порталов. Например, вам может потребоваться управлять фокусом клавиатуры, чтобы пользователь мог перемещать фокус в портал и из него естественным образом.
Следуйте Рекомендациям по созданию модальных диалогов 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://react.dev/reference/react-dom/createPortal