useGridList для списков-гридов

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


Импорт и базовая структура

Для использования useGridList необходимо подключить соответствующий модуль:

import { useGridList } from '@react-aria/grid';
import { useGridState } from '@react-stately/grid';
  • useGridState — хук состояния от React Stately, который управляет данными сетки, включая выбранные элементы, фокус и порядок отображения.
  • useGridList — отвечает за обработку ARIA-атрибутов, событий клавиатуры и взаимодействия с пользователем.

Простейший пример структуры:

function GridExample(props) {
  const state = useGridState(props);
  const ref = React.useRef();
  const { gridProps } = useGridList(props, state, ref);

  return (
    <div {...gridProps} ref={ref} role="grid">
      {state.collection.map(item => (
        <div key={item.key} role="gridcell">
          {item.rendered}
        </div>
      ))}
    </div>
  );
}
  • gridProps содержит все необходимые атрибуты ARIA и обработчики событий для контейнера сетки.
  • Каждый элемент коллекции получает role="gridcell" для соответствия спецификациям ARIA.

Управление состоянием сетки через React Stately

useGridList работает в связке с useGridState, который обеспечивает:

  • Коллекцию элементов (state.collection) с уникальными ключами.
  • Выбор элементов (state.selectionManager) с поддержкой одиночного и множественного выбора.
  • Фокусировку элементов (state.focusedKey) для навигации через клавиатуру.
  • Сортировку и фильтрацию при необходимости.

Пример передачи данных в состояние:

const items = [
  { id: '1', name: 'Элемент 1' },
  { id: '2', name: 'Элемент 2' },
  { id: '3', name: 'Элемент 3' },
];

const state = useGridState({
  selectionMode: 'multiple',
  collection: new Map(items.map(item => [item.id, { key: item.id, rendered: item.name }])),
});

Здесь каждый элемент коллекции должен иметь уникальный ключ (key) и содержимое (rendered), которое будет отображаться в сетке.


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

useGridList автоматически добавляет поддержку навигации клавишами:

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

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


ARIA-атрибуты и роли

useGridList назначает необходимые ARIA-атрибуты:

  • role="grid" для контейнера.
  • aria-multiselectable при множественном выборе.
  • role="row" и role="rowgroup" при использовании более сложной разметки с рядами.
  • role="gridcell" для отдельных элементов.

Пример с группировкой строк:

<div {...gridProps} ref={ref}>
  {state.collection.grouped.map(row => (
    <div role="row" key={row.key}>
      {row.items.map(item => (
        <div role="gridcell" key={item.key}>
          {item.rendered}
        </div>
      ))}
    </div>
  ))}
</div>

Настройка взаимодействия и кастомизация

useGridList предоставляет возможность передавать дополнительные параметры:

  • focusMode — управление поведением фокуса ('cell' или 'row').
  • selectionMode — режим выбора элементов ('none', 'single', 'multiple').
  • disabledKeys — массив ключей элементов, которые не должны быть интерактивными.
  • onSelectionChange — колбэк при изменении выбранных элементов.

Пример с обработкой выбора:

function handleSelectionChange(keys) {
  console.log('Выбраны элементы:', keys);
}

const { gridProps } = useGridList(
  { selectionMode: 'multiple', onSelectionChange: handleSelectionChange },
  state,
  ref
);

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

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

function CustomCard({ item, state }) {
  const isSelected = state.selectionManager.isSelected(item.key);
  return (
    <div
      role="gridcell"
      aria-selected={isSelected}
      style={{ border: isSelected ? '2px solid blue' : '1px solid gray' }}
    >
      {item.rendered}
    </div>
  );
}

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


Особенности работы с динамическими коллекциями

useGridList корректно обрабатывает динамически изменяющиеся данные:

  • Добавление и удаление элементов обновляет состояние сетки автоматически.
  • Фокус сохраняется на текущем элементе или переносится на следующий доступный элемент.
  • Выбранные элементы сохраняются, если их ключи остаются в коллекции.

Пример с динамическим обновлением:

const [items, setItems] = React.useState(initialItems);

function addItem() {
  setItems([...items, { id: String(items.length + 1), name: `Элемент ${items.length + 1}` }]);
}

useGridList при этом автоматически обновляет gridProps и состояние фокуса.


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

  • Использовать useGridList совместно с useGridState для полного управления коллекцией.
  • Назначать уникальные ключи каждому элементу для корректного отслеживания состояния.
  • Обеспечивать видимую индикацию выбранного и сфокусированного элемента.
  • Применять кастомные стили и компоненты через gridcell без изменения ARIA-логики.
  • Проверять поведение клавиатурной навигации и совместимость с экранными читалками.

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