useOverlay для оверлеев

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

Основная задача useOverlay — упрощение реализации доступных и предсказуемых оверлеев, соответствующих стандартам ARIA и UX.

import { useOverlay } from "@react-aria/overlays";
import { useRef } from "react";

function Modal({ isOpen, onClose, children }) {
  const ref = useRef();
  const { overlayProps } = useOverlay({ isOpen, onClose }, ref);

  return isOpen ? (
    <div {...overlayProps} ref={ref} className="modal">
      {children}
    </div>
  ) : null;
}

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


Параметры хука useOverlay

useOverlay принимает два аргумента:

  1. options — объект конфигурации, определяющий поведение оверлея.
  2. ref — ссылка на DOM-элемент оверлея.

Основные поля объекта options:

  • isOpen — логическое значение, определяющее открытость оверлея.
  • onClose — функция, вызываемая при закрытии оверлея (например, при клике вне или на Escape).
  • isDismissable — если true, оверлей можно закрыть кликом вне его границ.
  • isKeyboardDismissDisabled — запрещает закрытие оверлея по клавише Escape.
  • shouldCloseOnBlur — закрытие оверлея при уходе фокуса за его пределы.
  • isModal — если true, оверлей блокирует взаимодействие с контентом позади него.
const { overlayProps } = useOverlay(
  {
    isOpen,
    onClose: () => setOpen(false),
    isDismissable: true,
    isModal: true
  },
  overlayRef
);

Управление фокусом

Одна из ключевых функций useOverlay — правильная работа фокуса:

  • При открытии оверлея фокус автоматически перемещается на первый интерактивный элемент.
  • При закрытии фокус возвращается на элемент, который инициировал открытие.
  • Поддерживается trap focus для модальных оверлеев: пользователь не может случайно переместиться в фоновые элементы.
import { FocusScope } from "@react-aria/focus";

<FocusScope contain restoreFocus autoFocus>
  <div {...overlayProps} ref={overlayRef}>
    <button>Закрыть</button>
  </div>
</FocusScope>

FocusScope интегрируется с useOverlay, создавая полностью управляемый фокус внутри оверлея.


Закрытие оверлея

useOverlay поддерживает несколько способов закрытия:

  1. Escape — автоматически обрабатывается, если isKeyboardDismissDisabled не установлен.
  2. Клик вне оверлея — если isDismissable равен true.
  3. Программное закрытие — вызов onClose извне (например, кнопка внутри оверлея).
const { overlayProps } = useOverlay(
  {
    isOpen,
    onClose,
    isDismissable: true
  },
  overlayRef
);

overlayProps добавляет обработчики onKeyDown и onPointerDownOutside автоматически.


Интеграция с Portal

Оверлеи часто рендерятся в отдельном DOM-контейнере для корректного наложения на основной контент. useOverlay легко интегрируется с React Portal:

import { createPortal } from "react-dom";

return isOpen
  ? createPortal(
      <div {...overlayProps} ref={overlayRef} className="overlay">
        {children}
      </div>,
      document.body
    )
  : null;

Использование портала обеспечивает правильное позиционирование и управление z-index, не нарушая структуры приложения.


Сочетание с useOverlayPosition

Для всплывающих панелей и контекстных меню важно контролировать позицию оверлея относительно целевого элемента. Для этого useOverlay часто комбинируют с useOverlayPosition:

import { useOverlayPosition } from "@react-aria/overlays";
import { useRef } from "react";

const triggerRef = useRef();
const overlayRef = useRef();
const { overlayProps, placement } = useOverlayPosition({
  targetRef: triggerRef,
  overlayRef,
  placement: "bottom left"
});

<div ref={triggerRef}>Кнопка</div>
<div {...overlayProps} ref={overlayRef}>
  Меню
</div>

useOverlayPosition возвращает свойства для позиционирования и автоматически учитывает прокрутку, размеры окна и возможные коллизии.


Поддержка анимаций

Хотя useOverlay не предоставляет анимации «из коробки», он совместим с любыми CSS- или JS-анимациями. Благодаря тому, что оверлей остаётся в DOM до завершения анимации, можно создавать плавное появление и исчезновение:

<div
  {...overlayProps}
  ref={overlayRef}
  className={`overlay ${isOpen ? "fade-in" : "fade-out"}`}
>
  Контент
</div>

CSS:

.fade-in { opacity: 1; transition: opacity 0.3s; }
.fade-out { opacity: 0; transition: opacity 0.3s; }

Советы по использованию

  • Всегда использовать FocusScope внутри модальных оверлеев.
  • Для всплывающих подсказок можно отключать isDismissable или isModal в зависимости от UX.
  • Обрабатывать onClose централизованно для всех оверлеев приложения.
  • Сочетать с useOverlayPosition для динамического позиционирования.

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