Хуки состояния

Библиотека React Aria разделяет ответственность между поведенческими хуками и хуками состояния. Поведенческие хуки (useButton, useMenu, useListBox и другие) обеспечивают доступность, управление событиями и ARIA-атрибутами. Хуки состояния из пакета @react-stately управляют данными, выбором, навигацией, фокусом и изменениями состояния компонентов.

Такое разделение позволяет:

  • повторно использовать логику состояния
  • отделять представление от поведения
  • создавать собственные компоненты интерфейса без потери доступности

Хуки состояния реализуют модель, похожую на state machines, где каждое действие пользователя изменяет состояние структуры данных, а поведенческие хуки отражают эти изменения в DOM.


Основные пакеты состояния

Система состояния React Aria реализована в пакете:

@react-stately

Основные категории хуков:

Категория Назначение
Коллекции управление списками и элементами
Выбор управление выделением элементов
Компоненты форм состояние полей и вводов
Оверлеи модальные окна, всплывающие элементы
Навигация вкладки, меню, списки

Часто используемые хуки:

  • useToggleState
  • useOverlayTriggerState
  • useListState
  • useTreeState
  • useSingleSelectListState
  • useMultipleSelectionState
  • useComboBoxState
  • useMenuTriggerState
  • useTabsState

useToggleState

Назначение

useToggleState управляет булевым состоянием, которое может переключаться между true и false.

Используется для:

  • чекбоксов
  • переключателей
  • кнопок-переключателей
  • элементов раскрытия

Базовое использование

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

function ToggleExample() {
  let state = useToggleState({
    defaultSelected: false
  });

  return (
    <button onCl ick={state.toggle}>
      {state.isSelected ? "ON" : "OFF"}
    </button>
  );
}

Основные свойства

Свойство Описание
isSelected текущее состояние
setSelected установка состояния
toggle переключение

Параметры

useToggleState({
  defaultSelected,
  isSelected,
  onChange
})

defaultSelected

Начальное состояние.

defaultSelected: true

isSelected

Контролируемый режим.

useToggleState({
  isSelected: value,
  onChange: setValue
});

Пример контролируемого переключателя

function Switch({value, onChange}) {
  let state = useToggleState({
    isSelected: value,
    onChange
  });

  return (
    <button onCl ick={state.toggle}>
      {state.isSelected ? "Enabled" : "Disabled"}
    </button>
  );
}

useOverlayTriggerState

Назначение

useOverlayTriggerState управляет состоянием всплывающих элементов.

Используется для:

  • модальных окон
  • popover
  • dropdown
  • меню

Пример

import {useOverlayTriggerState} from '@react-stately/overlays';

function ModalExample() {
  let state = useOverlayTriggerState({});

  return (
    <>
      <button onCl ick={state.open}>Open</button>

      {state.isOpen && (
        <div className="modal">
          <button onCl ick={state.close}>Close</button>
        </div>
      )}
    </>
  );
}

Свойства состояния

Свойство Описание
isOpen открыто ли окно
open() открыть
close() закрыть
toggle() переключить

Параметры

useOverlayTriggerState({
  defaultOpen,
  isOpen,
  onOpenChange
})

Контролируемый режим

let state = useOverlayTriggerState({
  isOpen,
  onOpenChange: setOpen
});

useListState

Назначение

useListState управляет коллекциями элементов.

Используется для:

  • списков
  • dropdown
  • listbox
  • autocomplete
  • menu

Базовый пример

import {useListState} from '@react-stately/list';

function ListExample(props) {
  let state = useListState(props);

  return (
    <ul>
      {[...state.collection].map(item => (
        <li key={item.key}>
          {item.rendered}
        </li>
      ))}
    </ul>
  );
}

Структура коллекции

Каждый элемент коллекции содержит:

Node

Свойства:

Свойство Описание
key уникальный ключ
rendered отображаемый контент
value исходные данные
type тип узла

Параметры

useListState принимает свойства:

{
  items,
  children,
  selectionMode
}

Пример с данными

const items = [
  {id: 1, name: "Apple"},
  {id: 2, name: "Orange"},
  {id: 3, name: "Banana"}
];

function FruitList() {
  let state = useListState({
    items,
    children: item => (
      <Item key={item.id}>
        {item.name}
      </Item>
    )
  });

  return (
    <ul>
      {[...state.collection].map(item => (
        <li key={item.key}>
          {item.rendered}
        </li>
      ))}
    </ul>
  );
}

useMultipleSelectionState

Назначение

Управляет множественным выбором элементов.

Используется в:

  • таблицах
  • списках
  • галереях
  • multi-select

Пример

import {useMultipleSelectionState} from '@react-stately/selection';

let state = useMultipleSelectionState({
  selectionMode: "multiple"
});

Основные свойства

Свойство Описание
selectedKeys выбранные элементы
setSelectedKeys установка выбора
isSelected(key) проверка

Выбор элементов

state.setSelectedKeys(new Set(["1", "2"]));

Проверка выбора

state.isSelected("1");

useSingleSelectListState

Назначение

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

Примеры:

  • radio group
  • select
  • dropdown

Пример

import {useSingleSelectListState} from '@react-stately/list';

let state = useSingleSelectListState({
  defaultSelectedKey: "1"
});

Основные свойства

Свойство Описание
selectedKey выбранный элемент
setSelectedKey установка

Использование

state.setSelectedKey("2");

useComboBoxState

Назначение

Управляет состоянием комбинированного поля ввода с выбором.

Используется в:

  • autocomplete
  • sel ect с поиском
  • dropdown search

Пример

import {useComboBoxState} fr om '@react-stately/combobox';

let state = useComboBoxState({
  defaultFilter: contains,
  items
});

Основные свойства

Свойство Описание
inputValue текст поля
setInputValue изменить текст
selectedKey выбранный элемент
open() открыть список

Фильтрация

import {useFilter} from '@react-aria/i18n';

let {contains} = useFilter({sensitivity: 'base'});

useMenuTriggerState

Назначение

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

Используется вместе с:

  • useMenu
  • useMenuTrigger

Пример

import {useMenuTriggerState} from '@react-stately/menu';

let state = useMenuTriggerState({});

Основные методы

Метод Описание
open() открыть
close() закрыть
toggle() переключить

Пример меню

function MenuExample() {
  let state = useMenuTriggerState({});

  return (
    <>
      <button onCl ick={state.toggle}>
        Menu
      </button>

      {state.isOpen && (
        <div className="menu">
          Menu content
        </div>
      )}
    </>
  );
}

useTabsState

Назначение

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

Используется вместе с:

  • useTabs
  • useTabList
  • useTabPanel

Пример

import {useTabsState} from '@react-stately/tabs';

let state = useTabsState({
  defaultSelectedKey: "tab1"
});

Свойства

Свойство Описание
selectedKey активная вкладка
setSelectedKey переключение

Использование

state.setSelectedKey("tab2");

Архитектурные преимущества хуков состояния

Декларативность

Состояние описывается декларативно.

useTabsState({
  defaultSelectedKey: "tab1"
});

Повторное использование логики

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


Полная доступность

Поведенческие хуки React Aria автоматически используют состояние для:

  • управления фокусом
  • навигации клавиатурой
  • ARIA атрибутов

Контролируемый и неконтролируемый режим

Каждый хук поддерживает оба варианта.

Неконтролируемый:

defaultSelectedKey
defaultOpen

Контролируемый:

selectedKey
isOpen
onChange

Типичная архитектура компонента

Компоненты React Aria обычно состоят из двух уровней.

1. Состояние

let state = useListState(props);

2. Поведение

let {listBoxProps} = useListBox(props, state, ref);

Пример структуры

function ListBox(props) {
  let state = useListState(props);
  let ref = React.useRef();

  let {listBoxProps} = useListBox(props, state, ref);

  return (
    <ul {...listBoxProps} ref={ref}>
      {[...state.collection].map(item => (
        <Option key={item.key} item={item} state={state} />
      ))}
    </ul>
  );
}

Взаимодействие между состоянием и поведением

Схема работы:

User Interaction
       ↓
React Aria behavior hook
       ↓
State hook update
       ↓
React render
       ↓
DOM update

Ключевые принципы проектирования

Коллекции

Все компоненты списков используют единый интерфейс коллекций.


Универсальность

Одни и те же механизмы выбора работают для:

  • listbox
  • menu
  • table
  • grid

Управление клавиатурой

Состояние хранит:

  • активный элемент
  • выбранные элементы
  • порядок элементов

Доступность по умолчанию

Хуки автоматически поддерживают:

  • screen readers
  • клавиатурную навигацию
  • ARIA роли

Типичные паттерны использования

Компонент со списком

useListState
+
useListBox

Комбобокс

useComboBoxState
+
useComboBox

Меню

useMenuTriggerState
+
useMenuTrigger
+
useMenu

Вкладки

useTabsState
+
useTabs
+
useTabList
+
useTab

Рекомендации по использованию

Разделение состояния и UI

Хуки состояния должны использоваться до рендеринга элементов.


Минимизация состояния

Состояние должно хранить только:

  • данные
  • выбор
  • навигацию

Предсказуемые обновления

Все изменения состояния происходят через методы хуков.

setSelectedKey
setSelectedKeys
toggle
open
close

Использование ключей

Каждый элемент коллекции должен иметь уникальный key.

<Item key="id">

Итоговая архитектурная роль хуков состояния

Хуки состояния React Aria обеспечивают:

  • централизованное управление данными интерфейса
  • синхронизацию поведения компонентов
  • поддержку сложных взаимодействий
  • масштабируемость интерфейсной архитектуры
  • доступность пользовательских компонентов

Они формируют фундаментальную основу всей системы React Aria, позволяя строить сложные интерфейсы с полной поддержкой accessibility и управлением пользовательским взаимодействием на уровне абстракции состояния.