Spec-Zone.ru › React

createPortal

createPortal позволяет отображать дочерние элементы в другой части DOM.

<div>
  <SomeComponent />
  {createPortal(children, domNode, key?)}
</div>
  • Ссылка
    • createPortal(children, domNode, key?)
  • Использование
    • Отображение в другой части DOM
    • Отображение модального диалогового окна с порталом
    • Отображение компонентов React в разметке сервера, не использующей React
    • Отображение компонентов React в узлах DOM, не использующих React

Ссылка

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API