Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Що таке портали в React і як у них поводяться події?

Портал рендерить дочірні елементи в інший вузол DOM, ніж той, де знаходиться компонент:

import { createPortal } from 'react-dom';

function Modal({ open, onClose, children }) {
  if (!open) return null;

  return createPortal(
    <div className="fixed inset-0 grid place-items-center bg-black/50" onClick={onClose}>
      <div role="dialog" aria-modal="true" onClick={(e) => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body,
  );
}

Навіщо: модальні вікна, підказки, випадні меню мають бути поверх усього. Якщо їхня розмітка глибоко в дереві, батьківські стилі заважають: overflow: hidden обрізає, transform ламає position: fixed, z-index обмежений контекстом накладання батька.

Ключова особливість - події. Портал змінює місце в DOM, але не в дереві React. Події спливають за деревом React:

<div onClick={() => console.log('клік у батьку')}>
  <Modal open>
    <button>Кнопка у вікні</button>   {/* клік спливе до div батька! */}
  </Modal>
</div>

Клік по кнопці, яка в DOM лежить у body, все одно викличе onClick батька. Те саме з контекстом: портал бачить провайдери свого предка в React, а не за місцем у DOM.

Це зручно (стан, контекст і обробники працюють як для звичайних дітей), але інколи несподівано - наприклад, «клік поза меню» через contains() у DOM вважатиме клік у порталі зовнішнім.

Що ще потрібно для якісного модального вікна (портал цього не дає):

  • фокус усередині вікна і повернення фокусу після закриття;
  • закриття по Escape;
  • блокування прокрутки сторінки;
  • aria-modal, aria-labelledby.

Нативна альтернатива - <dialog> з showModal(): браузер сам виносить вікно на верхній шар (без порталу й боротьби із z-index), дає фокус і Escape. Для багатьох випадків простіше за портал.

SSR: на сервері порталів у document.body немає - модальні вікна зазвичай рендерять лише на клієнті (після монтування).

Докладніше в документації: createPortal

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання