createPortal
createPortal
дозволяє рендерити дочірні компоненти в інші частини DOM.
<div> <SomeComponent /> {createPortal(children, domNode, key?)} </div>
Опис
createPortal(children, domNode, key?)
Щоб створити портал, викличте createPortal
, передаючи JSX і DOM-вузол в якому він повинен відрендеритись:
import { createPortal } from 'react-dom'; // ... <div> <p>Цей дочірній елемент знаходиться в батьківському div.</p> {createPortal( <p>Цей дочірній елемент знаходиться безпосередньо в тілі документа.</p>, document.body )} </div>
Перегляньте більше прикладів нижче.
Портал лише змінює фізичне розташування DOM-вузла. У всіх інших випадках, JSX, який ви рендерите в портал, поводиться як дочірній елемент React-компонента, який його рендерить. Для прикладу, цей дочірній елемент має доступ до контексту, наданого батьківським деревом елементів, а події передаються вгору від дочірніх елементів до батьківських, відповідно до React-дереву компонентів.
Параметри
-
children
: Все, що може бути відрендерено за допомогою React, включаючи JSX (наприклад<div />
або<SomeComponent />
), Фрагмент (<>...</>
), рядок, число, або масив з них. -
domNode
: DOM-вузол, наприклад повернутий зdocument.getElementById()
. Переданий вузол вже повинен існувати. Передавання різних DOM-вузлів під час оновлення спричинить повторне створення контенту всередині порталу. -
опційний
key
: Унікальна рядкова або числова змінна, що використовується як ключ порталу.
Результат
createPortal
повертає React-вузол, який може бути включеним в JSX або ж повернутим з React-компонента. Якщо React зіткнеться з таким у виводі рендеру, він помістить надані children
всередину переданого domNode
.
Застереження
- Події з порталів поширюються згідно з деревом React-компонентів, а не DOM. Наприклад, якщо ви клікнете в межах порталу, обгорнутого в
<div onClick>
, то цей обробник подіїonClick
спрацює. Якщо така поведінка створює проблеми, зупиніть поширення події з порталу або ж перенесіть портал вище в дереві React-компонентів.
Використання
Рендер в іншу частину DOM
Портали дозволяють вашим компонентам рендерити деякі їхні дочірні елементи в інші частини DOM. Це дозволяє частині вашого компоненту “втекти” з будь-яких контейнерів, в яких вона перебуває. Приміром, компонент може відображати модальне вікно або спливаючу підказку, що з’являється поза та над основною частиною сторінки.
Щоб створити портал, відрендеріть результат createPortal
з JSX і DOM-вузлом, куди потрібно помістити JSX:
import { createPortal } from 'react-dom'; function MyComponent() { return ( <div style={{ border: '2px solid black' }}> <p>Цей дочірній елемент знаходиться в батьківському div.</p> {createPortal( <p>Цей дочірній елемент знаходиться безпосередньо в тілі документа.</p>, document.body )} </div> ); }
React помістить DOM-вузли переданого вами JSX всередину наданого вами DOM-вузла.
Без порталу, другий <p>
розміщувався би всередині батьківського <div>
, але портал “телепортував” його в document.body
:
Зверніть увагу, як другий параграф візуально знаходиться поза межами <div>
з рамкою. Якщо ви перевірите структуру DOM за допомогою інструментів розробника, то побачите, що другий <p>
розміщений безпосередньо в <body>
:
<body> <div id="root"> ... <div style="border: 2px solid black"> <p>Цей дочірній елемент знаходиться в батьківському div.</p> </div> ... </div> <p>Цей дочірній елемент знаходиться безпосередньо в тілі документа.</p> </body>
Портал лише змінює фізичне розташування DOM-вузла. У всіх інших випадках, JSX, який ви рендерите в портал, поводиться як дочірній елемент React-компонента, який його рендерить. Для прикладу, цей дочірній елемент має доступ до контексту, наданого батьківським деревом елементів, а події передаються вгору від дочірніх елементів до батьківських, відповідно до React-дереву компонентів.
Рендер модального вікна з допомогою порталу
Ви можете використовувати портал для створення модального вікна, що висітиме поверх решти сторінки, навіть якщо компонент, який викликає це вікно, знаходиться всередині контейнеру з overflow: hidden
або іншими стилями, які так чи інакше втручаються у модальне вікно.
У цьому прикладі два контейнери мають стилі, які обмежують відображення модального вікна. Проте, обмеження не впливає на вікно, відрендерене в порталі, тому що в DOM воно не знаходиться в межах батьківських JSX елементів.
Рендер React-компонентів у серверну розмітку, створену без використання React
Портали можуть бути корисними якщо ваш React-корінь це тільки частина статичної або відрендереної на сервері сторінки, не створеної з React. Наприклад, якщо ваша сторінка побудована з серверним фреймворком подібним до Rails, ви можете створити інтерактивні частини в середині статичних зон, приміром в бокових панелях. У порівнянні зі створенням кількох окремих React-коренів, портали дозволяють працювати з додатком як з єдиним React-деревом зі спільним станом, навіть якщо його окремі шматочки рендеряться в інші частини DOM.
Рендер 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>Привіт від React!</p>, popupContainer )} </div> );
Ось повний приклад з яким ви можете експерементувати: