Spec-Zone.ru › React 18

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>
    </>
  );
}

Возможные проблемы

Важно убедиться, что ваш интерфейс доступен при использовании порталов. Например, вам может потребоваться управлять фокусом клавиатуры, чтобы пользователь мог перемещать фокус в портал и из него естественным образом.

Следуйте Рекомендациям по разработке модальных диалогов 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

Spec-Zone.ru

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