useSelectableItem для элементов

useSelectableItem — это хук из библиотеки React Aria, предназначенный для управления состоянием и поведением отдельного элемента внутри селектируемого списка, например, в ListBox, Grid или любом кастомном компоненте с выбором элементов. Основная задача хука — интегрировать элемент с системой выделения (selection) и обеспечивать корректную работу с клавиатурой и доступностью.


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

import {useSelectableItem} from '@react-aria/selection';
import {useRef} from 'react';

function Item({item, state}) {
  const ref = useRef();
  const {itemProps, isSelected, isDisabled} = useSelectableItem({
    key: item.key,
    selectionManager: state.selectionManager,
    shouldSelectOnPressUp: true,
    isDisabled: item.isDisabled
  }, state);

  return (
    <div {...itemProps} ref={ref} style={{
      background: isSelected ? 'lightblue' : 'white',
      opacity: isDisabled ? 0.5 : 1,
      padding: 4,
      margin: 2,
      border: '1px solid #ccc'
    }}>
      {item.name}
    </div>
  );
}

Пояснения:

  • key — уникальный идентификатор элемента в списке.
  • selectionManager — объект, управляющий состоянием выделения всех элементов. Обычно передаётся из состояния списка (useListState или useGridState).
  • shouldSelectOnPressUp — определяет момент выбора: при отпускании кнопки мыши (true) или при нажатии (false).
  • isDisabled — блокирует элемент, делая его недоступным для выбора.

Возвращаемые значения

Хук useSelectableItem возвращает объект с тремя ключевыми свойствами:

  1. itemProps — набор атрибутов, которые необходимо передать корневому DOM-элементу элемента списка. Включает:

    • role — роль ARIA (option в ListBox, row в Grid).
    • aria-selected — текущий статус выделения.
    • События для мыши и клавиатуры (onClick, onKeyDown, onFocus и др.).
  2. isSelected — булево значение, отражающее, выделен ли элемент в данный момент. Используется для визуального отображения состояния.

  3. isDisabled — булево значение, показывающее, доступен ли элемент для выбора. Используется для стилизации и блокировки событий.


Интеграция с useListState и useGridState

Для полноценной работы выделяемого списка необходимо создать состояние через useListState (или useGridState для сеток):

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

function List({items}) {
  const state = useListState({items});

  return (
    <div role="listbox">
      {items.map(item => (
        <Item key={item.key} item={item} state={state} />
      ))}
    </div>
  );
}

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

  • state.selectionManager передаётся в каждый вызов useSelectableItem.
  • useSelectableItem автоматически синхронизируется с этим менеджером и обновляет isSelected при изменении состояния списка.
  • Позволяет реализовать множественный выбор (selectionMode: 'multiple') и выбор с клавиатуры без ручной работы с событиями.

Работа с клавиатурой

useSelectableItem обрабатывает стандартные ARIA-паттерны взаимодействия:

  • Arrow Up / Arrow Down — перемещение по списку.
  • Space / Enter — выбор элемента.
  • Home / End — переход к первому/последнему элементу списка.

При этом, если элемент isDisabled, события клавиатуры игнорируются, что предотвращает некорректное выделение.


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

1. Элемент с кастомной логикой выбора

const {itemProps, isSelected} = useSelectableItem({
  key: item.id,
  selectionManager: state.selectionManager,
  isDisabled: item.locked,
  selectionBehavior: 'toggle'
}, state);
  • selectionBehavior позволяет управлять поведением выбора:

    • replace — замена текущего выделения.
    • toggle — переключение состояния (подходит для множественного выбора).
    • extend — расширение выделения (используется с Shift).

2. Стилизация выбранных элементов

<div {...itemProps} style={{
  background: isSelected ? '#0078D4' : '#fff',
  color: isSelected ? '#fff' : '#000',
  cursor: isDisabled ? 'not-allowed' : 'pointer'
}}>
  {item.label}
</div>
  • isSelected и isDisabled позволяют легко визуально различать состояние каждого элемента.
  • Можно комбинировать с CSS-классами для сложных интерфейсов.

Особенности работы с динамическими списками

Если элементы добавляются или удаляются динамически, useSelectableItem корректно синхронизируется с selectionManager. Главное — обеспечить уникальность key для каждого элемента. Это позволяет:

  • Поддерживать состояние выделения при обновлении списка.
  • Использовать анимации добавления/удаления без потери выбранных элементов.

События мыши и сенсорных устройств

useSelectableItem автоматически обрабатывает:

  • Нажатие (onPressStart, onPressEnd) с учетом shouldSelectOnPressUp.
  • Долгое нажатие (longPress) для мобильных интерфейсов.
  • Предотвращение выбора текста при клике.

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


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

  • Использовать React.memo для компонентов элементов, чтобы минимизировать перерисовки при изменении состояния других элементов списка.
  • Передавать в useSelectableItem только необходимые свойства (key, isDisabled), чтобы хук не пересчитывался без необходимости.
  • В больших списках применять виртуализацию (react-virtual или аналогичные библиотеки) совместно с useSelectableItem для оптимальной работы.

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