Согласно официальной документации по React, порталы представляют собой первоклассный способ отображения дочерних компонентов в узел DOM вне DOM-иерархии родительского компонента, определяемой иерархией дерева компонентов. Порталы применяют в ситуациях, когда необходимо, чтобы дочерние компоненты визуально вырывались из родительского контейнера. Типичные примеры использования порталов:

  • Модальные диалоговые окна.
  • Всплывающие подсказки.
  • Всплывающие визитки.
  • Загрузчики.

Портал создаётся с помощью ReactDOM.createPortal(child, container). Здесь child —  это элемент, фрагмент или строка React, а container—  это местоположение или узел DOM, в который должен добавляться портал.

Вот пример компонента modal, созданного с использованием приведённого выше API:

const Modal =({ message, isOpen, onClose, children })=> {
  if (!isOpen) return null
  return ReactDOM.createPortal(    
    <div className="modal">
      <span className="message">{message}</span>
      <button onClick={onClose}>Close</button>
    </div>,
    domNode)
}

Несмотря на то, что портал отображается вне родительского элемента DOM, ведёт он себя аналогично обычному компоненту React внутри приложения. Он может получить доступ к props и context API. Почему? Потому что портал находится внутри иерархии дерева React.

С реальным примером порталов в React можно ознакомиться в этом компоненте, выложенном в разделе компонентов на сайте Bit (вы тоже можете задействовать этот сайт для выкладывания там компонентов и повторного их использования):

Для чего нужны порталы

При использовании modal внутри конкретного элемента (родительского компонента) высота и ширина этого modal будут унаследованы от компонента, в котором находится modal. Поэтому есть вероятность того, что modal будет «обрезан», вследствие чего он будет неправильно отображаться в приложении. Во избежание этой проблемы традиционному modal потребуются такие свойства стиля CSS, как overflow:hidden и z-index.

Типичный «modal», в котором родительский компонент переопределяет высоту и ширину

В результате приведённого выше примера кода, modal будет отображён в root внутри вложенных компонентов. При просмотре кода приложения в браузере с помощью функции inspect элементы будут показаны следующим образом:

«modal», отображаемый без портала в React.

Посмотрим, как нам здесь задействовать портал React. Следующий код решает этот вопрос через createPortal()для создания DOM-узла вне иерархии root:

const Modal =({ message, isOpen, onClose, children })=> {
  if (!isOpen) return null;
  return ReactDOM.createPortal(
     <div className="modal">
      <span>{message}</span>
      <button onClick={onClose}>Close</button>
     </div>
    ,document.body);
  }function Component() {
  const [open, setOpen] = useState(false)
  return (
    <div className="component">
      <button onClick={() => setOpen(true)}>Open Modal</button>
      <Modal 
       message="Hello World!" 
       isOpen={open} 
       onClose={() => setOpen(false)}
      />
    </div>
  )
}

Ниже показана иерархия дерева DOM, которая будет получена при использовании порталов React. В ней modal будет добавлен вне root, находясь с ним на одном уровне:

«modal», отображаемый с порталом в React.

Размеры компонента modal не будут унаследованы или изменены родительскими компонентами, потому что он отображается вне корневой иерархии root.

«modal», отображаемый как портал

Этот пример можно найти в CodeSandbox и поэкспериментировать с кодом, чтобы воочию убедиться в том, как работают порталы, и проработать рассмотренные нами вопросы.

Что надо учитывать при использовании порталов

Имея дело с порталами в React, надо помнить о нескольких вещах. Эти поведения не сразу заметишь, если они тебе не знакомы. Поэтому немного расскажем и о них тоже.

  • Всплывающее событие будет работать, как обычно, распространяя события на предков в дереве React, независимо от местоположения узла портала в DOM.
  • React контролирует узлы портала и его жизненный цикл при отображении дочерних элементов с помощью этих порталов.
  • Порталы влияют только на структуру DOM для HTML и не затрагивают дерево компонентов React.
  • Предварительно определяется точка монтирования HTML: при использовании порталов необходимо определить HTML-элемент DOM в качестве точки монтирования компонента портала.

Заключение

Порталы в React могут пригодиться, когда необходимо отобразить дочерние компоненты вне обычной иерархии DOM. При этом используется иерархия дерева компонентов React и не нарушается поведение по умолчанию, определённое для распространения событий. Таким образом отображаются такие компоненты, как modal, всплывающие подсказки или сообщения и многие другие.

Более подробная информация о порталах содержится в официальной документации React.

Спасибо за внимание!

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Madushika Perera: Understanding React Portals and Its Use-Cases

Предыдущая статьяРуководство по принципам моушн-дизайна
Следующая статьяПодписки, чеки и StoreKit в iOS 14