useDialog для диалогов

useDialog — это хук из библиотеки React Aria, предназначенный для управления доступностью диалоговых окон (модальных окон) в приложениях на React. Он обеспечивает корректную работу с экранными читалками и клавиатурной навигацией, автоматически добавляя необходимые ARIA-атрибуты и управление фокусом. Хук не предоставляет визуальную часть компонента, он отвечает только за поведение и доступность.

Простейший вызов хука выглядит так:

import { useDialog } from '@react-aria/dialog';
import { useOverlay } from '@react-aria/overlays';
import { useRef } from 'react';

function Modal({ title, isOpen, onClose, children }) {
  let ref = useRef();
  let { dialogProps, titleProps } = useDialog({ role: 'dialog', 'aria-label': title }, ref);

  if (!isOpen) return null;

  return (
    <div {...dialogProps} ref={ref}>
      <h2 {...titleProps}>{title}</h2>
      {children}
      <button onCl ick={onClose}>Закрыть</button>
    </div>
  );
}

В этом примере useDialog возвращает объект dialogProps для контейнера диалога и titleProps для заголовка. Эти свойства включают все необходимые атрибуты ARIA и управление фокусом для доступности.


Параметры useDialog

Хук принимает два аргумента: объект с опциями и ref на элемент диалога.

Основные опции:

  • role — роль диалога, чаще всего 'dialog' или 'alertdialog'. 'alertdialog' используется для критических сообщений, требующих немедленного внимания пользователя.
  • 'aria-label' или 'aria-labelledby' — для заголовка диалога. Можно использовать один из двух способов, но aria-labelledby предпочтительнее, если заголовок представлен отдельным элементом.
  • isDismissable — булевое значение. Если true, диалог можно закрыть кликом вне его области или клавишей Escape.
  • onClose — функция, вызываемая при закрытии диалога.
  • UNSAFE_style и UNSAFE_className — позволяют задать стили и классы, но их использование не влияет на доступность.

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

Одним из ключевых преимуществ useDialog является управление фокусом при открытии и закрытии модального окна. При открытии:

  • Фокус автоматически перемещается на первый фокусируемый элемент внутри диалога.
  • Если внутри нет элементов с фокусом, фокус устанавливается на контейнер диалога.
  • При закрытии диалога фокус возвращается к элементу, который инициировал его открытие.

Для интеграции с overlay-логикой, например, при затемнении фона или блокировке прокрутки, рекомендуется использовать useOverlay:

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

let { overlayProps } = useOverlay({ isOpen, onClose, isDismissable: true }, ref);

overlayProps добавляются к контейнеру, чтобы обеспечить закрытие по Escape или клику вне модального окна.


Взаимодействие с клавиатурой

useDialog автоматически поддерживает следующие сценарии:

  • Escape — закрытие диалога, если isDismissable = true.
  • Tab / Shift+Tab — циклическая навигация по фокусируемым элементам внутри диалога (focus trap).
  • Enter / Space — активирует элементы управления, такие как кнопки.

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


Роли и семантика

Правильная роль диалога критична для доступности:

  • role="dialog" — стандартный диалог, не требует немедленного внимания.
  • role="alertdialog" — важное сообщение, требующее подтверждения. Важно, что экранные читалки автоматически объявляют его содержание сразу после открытия.

Заголовок должен быть связан с диалогом через aria-labelledby:

<h2 id="dialog-title">{title}</h2>
<div {...dialogProps} aria-labelledby="dialog-title" ref={ref}>
  ...
</div>

Интеграция с React Aria Overlay

Для полнофункциональных модальных окон рекомендуется комбинировать useDialog с useOverlay:

import { useOverlay, OverlayProvider } from '@react-aria/overlays';

function Modal({ isOpen, onClose, children }) {
  let ref = useRef();
  let { overlayProps } = useOverlay({ isOpen, onClose, isDismissable: true }, ref);
  let { dialogProps, titleProps } = useDialog({ role: 'dialog', 'aria-label': 'Модальное окно' }, ref);

  if (!isOpen) return null;

  return (
    <div {...overlayProps}>
      <div {...dialogProps} ref={ref}>
        <h2 {...titleProps}>Заголовок</h2>
        {children}
      </div>
    </div>
  );
}

Использование OverlayProvider позволяет корректно управлять несколькими модальными окнами и приоритетами фокуса в сложных интерфейсах.


Работа с динамическим контентом

Если содержимое диалога генерируется динамически, важно, чтобы все фокусируемые элементы были доступны сразу после рендера. React Aria автоматически обеспечивает корректную работу focus trap и анонсирование экранными читалками, даже если элементы появляются с задержкой через useEffect или загрузку данных.


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

  • Всегда указывать заголовок через aria-label или aria-labelledby для улучшения доступности.
  • Использовать isDismissable = true, чтобы пользователи могли закрыть диалог легко.
  • Сочетать useDialog с useOverlay, если требуется блокировка фона или управление несколькими модальными окнами.
  • Не забывать возвращать фокус к элементу, открывшему диалог, особенно в сложных интерфейсах с несколькими модальными окнами.
  • Для критических сообщений применять role="alertdialog" и выделять их визуально.

useDialog в связке с useOverlay обеспечивает полный контроль над диалогами с соблюдением всех стандартов доступности, позволяя создавать сложные и удобные интерфейсы без необходимости вручную прописывать ARIA-атрибуты и логику фокуса.