useSelect для выпадающих списков

Хук useSelect предназначен для создания доступных и полностью управляемых выпадающих списков в React-приложениях. Он обеспечивает взаимодействие с пользователем через клавиатуру, мышь и вспомогательные технологии, автоматически добавляя необходимые атрибуты ARIA.

Базовый синтаксис хука:

const { labelProps, triggerProps, valueProps, menuProps } = useSelect(options);

Здесь options — это объект конфигурации, который включает:

  • label — текст метки для селекта.
  • selectedKey — текущий выбранный ключ.
  • onSelectionChange — функция, вызываемая при изменении выбора.
  • items — массив объектов, представляющих элементы списка ({ key, name }).
  • disabledKeys — массив ключей элементов, которые должны быть недоступны.

Управление состоянием селекта

useSelect не хранит внутреннее состояние выбранного элемента, а предоставляет механизмы для управления им извне. Для этого обычно используют хук useState:

const [selectedKey, setSelectedKey] = useState(null);

const { labelProps, triggerProps, valueProps, menuProps } = useSelect({
  items,
  selectedKey,
  onSelectionChange: setSelectedKey
});

Такой подход позволяет интегрировать useSelect в глобальные состояния приложения, синхронизировать выбор с сервером или другими компонентами.

Свойства, предоставляемые useSelect

  • labelProps — атрибуты для <label> компонента. Обеспечивает корректную связь метки и поля ввода.
  • triggerProps — свойства кнопки или элемента, открывающего меню. Содержит обработку кликов, клавиш и фокуса.
  • valueProps — свойства контейнера выбранного значения. Используются для отображения текущего выбора.
  • menuProps — атрибуты для <ul> или контейнера меню. Включает управление видимостью, фокусом и навигацией клавиатурой.

Создание выпадающего списка с useSelect

Простейший компонент селекта:

function SelectExample() {
  const items = [
    { key: 'apple', name: 'Яблоко' },
    { key: 'banana', name: 'Банан' },
    { key: 'cherry', name: 'Вишня' }
  ];

  const [selectedKey, setSelectedKey] = useState(null);

  const { labelProps, triggerProps, valueProps, menuProps } = useSelect({
    items,
    selectedKey,
    onSelectionChange: setSelectedKey
  });

  return (
    <div>
      <label {...labelProps}>Выберите фрукт</label>
      <button {...triggerProps}>
        <span {...valueProps}>
          {items.find(item => item.key === selectedKey)?.name || 'Выберите...'}
        </span>
      </button>
      {menuProps.isOpen && (
        <ul {...menuProps}>
          {items.map(item => (
            <li key={item.key} {...menuProps.getItemProps({ key: item.key })}>
              {item.name}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Здесь menuProps.isOpen управляет видимостью меню, а метод getItemProps добавляет каждому элементу необходимые ARIA-атрибуты и обработчики событий.

Навигация и клавиатурная доступность

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

  • Открытие меню через Enter или Space.
  • Перемещение по элементам стрелками ArrowUp / ArrowDown.
  • Выбор элемента через Enter.
  • Закрытие меню через Escape или клик вне области.

Все эти действия встроены в возвращаемые свойства triggerProps и menuProps, что позволяет создать полностью доступный селект без дополнительного кода.

Доступность и ARIA

Компоненты на базе useSelect получают следующие преимущества:

  • Атрибут aria-haspopup="listbox" на триггере.
  • Связь метки с полем через aria-labelledby.
  • Атрибут aria-expanded указывающий, открыто меню или нет.
  • Поддержка role=“listbox” для контейнера меню и role=“option” для элементов списка.

Это обеспечивает корректную работу с экранными читалками и вспомогательными технологиями.

Дополнительные возможности

  • Disabled Items: список элементов может содержать недоступные ключи через disabledKeys.
  • Custom Rendering: каждый элемент можно рендерить с кастомной разметкой, сохраняя ARIA-связь.
  • Controlled/Uncontrolled: useSelect можно использовать как полностью управляемый или частично контролируемый хук.

Интеграция с другими хуками React Aria

useSelect часто комбинируется с:

  • useOverlay — для позиционирования меню над другими элементами.
  • useFocusRing — для визуального отображения фокуса при навигации клавишами.
  • useButton — для улучшенной обработки кнопки-триггера.

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

Резюме ключевых свойств

  • items — массив элементов списка.
  • selectedKey — выбранный элемент.
  • onSelectionChange — обработчик изменения выбора.
  • labelProps, triggerProps, valueProps, menuProps — набор атрибутов и обработчиков для корректной работы и доступности.
  • disabledKeys — управление недоступными элементами.

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