useToggleButton для переключателей

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


Импорт и базовое использование

Для работы с useToggleButton необходимо импортировать хук и useToggleState для управления состоянием:

import { useToggleButton } from '@react-aria/button';
import { useToggleState } from '@react-stately/toggle';

Пример базового переключателя:

function ToggleExample() {
  let state = useToggleState({ defaultSelected: false });
  let ref = React.useRef();
  let { buttonProps, isPressed } = useToggleButton({ isSelected: state.isSelected, onChange: state.toggle }, ref);

  return (
    <button {...buttonProps} ref={ref}>
      {isPressed ? 'Включено' : 'Выключено'}
    </button>
  );
}

Ключевые моменты:

  • useToggleState управляет внутренним состоянием компонента (isSelected).
  • useToggleButton добавляет корректные ARIA-атрибуты (aria-pressed) и обработчики событий.
  • buttonProps содержит все необходимые свойства для элемента <button>.
  • isPressed позволяет визуально изменять контент кнопки в зависимости от состояния.

Работа с buttonProps

buttonProps включает:

  • aria-pressed — отражает текущее состояние переключателя (true или false).
  • onPress — универсальный обработчик кликов и нажатий клавиш.
  • id и ref — обеспечивают корректную привязку к DOM и поддержку фокуса.
  • disabled — позволяет блокировать кнопку.

Использование buttonProps гарантирует соответствие стандартам доступности без необходимости вручную прописывать атрибуты ARIA.


Настройка состояния

useToggleState предоставляет следующие параметры:

let state = useToggleState({
  defaultSelected: false, // начальное состояние
  isSelected: undefined,  // управляемое состояние
  onChange: (selected) => console.log(selected) // колбэк при изменении
});

Особенности:

  • defaultSelected задаёт начальное состояние для неуправляемого компонента.
  • isSelected позволяет полностью контролировать состояние снаружи.
  • onChange вызывается при каждом изменении состояния, что удобно для синхронизации с глобальным состоянием или API.

Поддержка клавиатуры и фокуса

useToggleButton автоматически обрабатывает клавиатурные события:

  • Enter и Space — переключение состояния кнопки.
  • Поддержка фокуса через ref, что обеспечивает корректную навигацию с клавиатуры.

Эта автоматизация избавляет от необходимости вручную писать обработчики onKeyDown и onFocus.


Интеграция с кастомными компонентами

useToggleButton можно использовать не только с <button>, но и с другими элементами, выполняющими роль переключателя:

function CustomToggle() {
  let state = useToggleState({ defaultSelected: true });
  let ref = React.useRef();
  let { buttonProps } = useToggleButton({ isSelected: state.isSelected, onChange: state.toggle }, ref);

  return (
    <div {...buttonProps} ref={ref} style={{ padding: '10px', border: '1px solid black', cursor: 'pointer' }}>
      {state.isSelected ? 'Активно' : 'Неактивно'}
    </div>
  );
}

Особенности:

  • ARIA-атрибуты корректно применяются к любому элементу.
  • Можно стилизовать компонент по своему усмотрению.
  • Сохраняется доступность и поддержка клавиатуры.

Состояние блокировки и индикация

Для реализации заблокированных кнопок используется стандартный атрибут isDisabled:

let { buttonProps } = useToggleButton({ isSelected: state.isSelected, onChange: state.toggle, isDisabled: true }, ref);

В этом случае:

  • aria-disabled автоматически добавляется к элементу.
  • Нажатия и клавиатурные события игнорируются.
  • Визуально кнопка может быть стилизована как неактивная.

Примеры продвинутых сценариев

  1. Синхронизация нескольких переключателей:
const [master, setMaster] = React.useState(false);
const childStates = [true, false, true].map((val) => useToggleState({ defaultSelected: val }));

const toggleAll = () => {
  setMaster(!master);
  childStates.forEach((state) => state.setSelected(!master));
};
  1. Комбинация с анимациями:

isPressed можно использовать для запуска анимации включения/выключения:

<div className={`toggle-slider ${isPressed ? 'on' : 'off'}`}></div>
  1. Управляемый компонент с внешним состоянием:
function ControlledToggle({ selected, onToggle }) {
  let ref = React.useRef();
  let { buttonProps } = useToggleButton({ isSelected: selected, onChange: onToggle }, ref);
  return <button {...buttonProps} ref={ref}>{selected ? 'Вкл' : 'Выкл'}</button>;
}

Лучшие практики

  • Использовать useToggleState для управления состоянием внутри компонентов.
  • Передавать buttonProps напрямую на рендеримый элемент.
  • Для нестандартных элементов всегда указывать ref, чтобы сохранялась поддержка клавиатуры.
  • Всегда проверять isDisabled для кнопок, блокирующих взаимодействие.
  • Сохранять визуальную индикацию через isPressed для улучшения UX.

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