React — это популярная библиотека JavaScript для создания пользовательских интерфейсов. Одной из мощных функций React является использование порталов, которые позволяют разработчикам отображать контент за пределами обычной древовидной структуры React. В этой статье вы узнаете все о том, что такое порталы, как их использовать, а также о некоторых распространенных случаях использования порталов в приложениях React.

Что такое порталы?

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

С технической точки зрения, порталы в React работают, создавая новый узел DOM и отображая в нем компонент. Этот новый узел добавляется к элементу body документа HTML, который находится за пределами обычного дерева компонентов React. Визуализированное содержимое может быть расположено и оформлено независимо от родительского компонента.

Преимущества использования порталов

Использование порталов в React имеет несколько преимуществ:

  • Гибкость. Порталы позволяют отображать компонент за пределами его родительского контейнера, что может быть полезно в ситуациях, когда вам нужно отобразить содержимое в другой части страницы.
  • Улучшенная производительность. Отрисовывая компонент вне его родительского контейнера, вы можете уменьшить количество манипуляций с DOM, необходимых для обновления компонента, что может повысить производительность.

Распространенные варианты использования порталов

Порталы можно использовать для различных элементов пользовательского интерфейса. Вот несколько распространенных вариантов использования порталов в React:

  • Модальные
  • Диалоговые окна
  • Подсказки
  • Подсказки при наведении
  • Поповеры
  • Выпадающие меню
  • Погрузчики

Как создать портал в React

Чтобы создать портал в React, мы используем createPortal() API. Этот метод принимает два аргумента:

  1. children: любой допустимый элемент React, который можно отобразить.
  2. domNode: DOM-контейнер, в котором должен отображаться children.

Вот синтаксис:

createPortal(children, domNode);

Пример:

import { createPortal } from "react-dom";

const Modal = () => {
  return (
    <div className="modal">
      <h1>Modal Content</h1>
    </div>
  );
};

const Portal = () => {
  return createPortal(<Modal />, document.getElementById("modal-root"));
};

В приведенном выше примере мы определяем модальный компонент, который хотим визуализировать с помощью портала. Затем мы определяем компонент портала, который использует метод createPortal() для рендеринга модального компонента в контейнере с идентификатором modal-root.

Как использовать порталы

Чтобы увидеть, как работают порталы, давайте создадим простое приложение React, которое использует порталы для рендеринга модального компонента. Если вы работаете над существующим проектом, вы можете пропустить первый шаг. Полный код этого примера доступен на GitHub.

Шаг 1. Инициализируйте приложение React

Сначала создайте новое приложение React с помощью Vite:

npm create vite@latest my-react-app --template react

Перейдите в каталог проекта и установите зависимости:

cd my-react-app 
npm install 
npm run dev

Шаг 2: Создайте контейнер портала

Затем создайте элемент контейнера в файле index.html, в котором будет размещаться портал. Этот элемент должен быть расположен за пределами корневого узла приложения React, который обычно представляет собой div с идентификатором root.

В этом примере мы создадим div с идентификатором portal и добавим его в тело HTML-файла:

<body>
  <div id="root"></div>
  <div id="portal"></div>
</body>

Шаг 3: Создайте компонент Overlay

Мы будем использовать компонент Overlay в качестве эффекта фона при открытии модального окна, а также создадим внутри него портал.

Создайте новый файл Overlay.jsx в каталоге src и вставьте следующий код:

import React from "react";
import { createPortal } from "react-dom";
import "./App.css";

const Overlay = ({ closeModal, children }) => {
  return createPortal(
    <div className="overlay" onClick={closeModal}>
      {children}
    </div>,
    document.getElementById("portal")
  );
};

export default Overlay;

Компонент Overlay возвращает фрагмент JSX, который использует метод createPortal() из пакета react-dom для рендеринга div с дочерними элементами внутри портала.

Затем мы устанавливаем событие onClick для div, чтобы вызвать функцию closeModal, которая передается как свойство.

Шаг 4: Создайте модальный компонент

Затем создайте новый файл Modal.jsx в каталоге src и добавьте следующий код.

import React from "react";
import Overlay from "./Overlay";
import "./App.css";

const Modal = ({ closeModal }) => {
  return (
    <Overlay closeModal={closeModal}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <h3>Modal</h3>
        <p>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Odit, libero.
          Exercitationem quod quam perspiciatis quasi voluptate sint facilis
          quia praesentium pariatur veniam quidem nam, ullam porro eveniet
          molestiae minima eligendi.
        </p>
        <button onClick={closeModal}>Close</button>
      </div>
    </Overlay>
  );
};

export default Modal;

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

Кроме того, событие onClick для элемента кнопки вызовет функцию closeModal, которая была передана в качестве реквизита.

Шаг 5. Отредактируйте компонент приложения

Скопируйте и вставьте следующий код в файл App.jsx.

import { useState } from "react";
import Modal from "./Modal";
import "./App.css";

function App() {
  // State to manage whether the modal is open or closed
  const [modalOpen, setModalOpen] = useState(false);

  // Function to toggle the state between open and closed
  const handleModal = () => {
    setModalOpen(!modalOpen);
  };

  return (
    <div className="App">
      <h1>React Modal Demo</h1>
      <p>
        This is a demo of how to use portals to create a modal component in
        React.
      </p>
      <button onClick={handleModal}>Open Modal</button>

      {modalOpen && <Modal closeModal={() => setModalOpen(false)} />}
    </div>
  );
}

export default App;

Здесь мы определяем состояние для управления тем, открыто ли модальное окно или закрыто, и функцию для переключения состояния. При нажатии кнопки «Открыть модальный» вызывается функция handleModal, и состояние modalOpen соответственно обновляется.

Если состояние равно true, визуализируется модальный компонент, который включает кнопку «Закрыть», которая устанавливает для состояния modalOpen значение false.

Шаг 6: Стиль приложения

Наконец, давайте стилизуем приложение. Добавьте следующий код в файл App.css:

Вот как выглядит финальное приложение:

Теперь давайте подробнее рассмотрим, как это работает. Откройте Инструменты разработчика в браузере и проверьте страницу. Вы заметите, что пока модальное окно закрыто, div portal пуст.

Когда вы нажимаете кнопку, чтобы открыть модальное окно, оно отображается внутри div portal, как мы и указали.

Новые узлы DOM теперь монтируются внутри узла portal вместо узла App, указанного в исходном коде. Это демонстрирует функциональность порталов в действии.

Всплытие событий и порталы

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

Поскольку порталы влияют только на структуру HTML DOM, а не на дерево компонентов React, такие функции, как Context API и пузырьковое воспроизведение событий, продолжают работать, как и раньше.

Чтобы предотвратить всплытие событий, как показано в примере, вы можете использовать метод stopPropagation(), чтобы убедиться, что срабатывает только прослушиватель событий в контейнере портала.

Подведение итогов

Порталы в React — это мощная функция, которая позволяет разработчикам отображать контент за пределами обычной иерархии компонентов. Это может быть полезно для создания модальных окон, всплывающих окон и других элементов пользовательского интерфейса, которые необходимо позиционировать относительно определенного элемента или отображать поверх основного интерфейса приложения.

При использовании порталов важно помнить о некоторых передовых методах.

  • Во-первых, всегда очищайте узел портала при размонтировании компонента. Это можно сделать с помощью хука useEffect и метода removeChild.
  • Во-вторых, будьте осторожны, чтобы не использовать порталы чрезмерно, так как они могут усложнить ваш код.
  • Наконец, всегда тщательно проверяйте свои порталы на наличие проблем с доступностью, поскольку иногда они могут вызывать неожиданное поведение в определенных браузерах или программах чтения с экрана. Обязательно следуйте Практикам создания модальных окон WAI-ARIA при работе с модальными окнами.

Спасибо за чтение! Я надеюсь, что вы нашли эту статью полезной. Если да, то ставьте лайк и делитесь с друзьями и коллегами :)

Я хотел бы связаться с вами на Twitter | ЛинкедИн | Гитхаб | "Портфолио".

Удачного кодирования!

Ресурсы

Первоначально опубликовано на https://israelmitolu.hashnode.dev.