useTableHeaderRow и useTableRow

Библиотека React Aria предоставляет мощные хуки для создания доступных компонентов пользовательского интерфейса. Среди них useTableHeaderRow и useTableRow предназначены для работы с таблицами, обеспечивая правильную семантику, управление фокусом и поддержку клавиатурной навигации.


useTableHeaderRow

useTableHeaderRow используется для создания строки заголовка таблицы (<thead> / <tr>), обеспечивая корректную разметку и доступность. Этот хук автоматически добавляет необходимые ARIA-атрибуты, учитывая структуру таблицы и сортируемость столбцов.

Синтаксис:

const { rowProps } = useTableHeaderRow({ node, state });
  • node — объект типа TableHeaderRowNode из состояния таблицы (useTableState), описывающий заголовок строки.
  • state — объект состояния таблицы, управляемый хуком useTableState.

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

  • rowProps — свойства, которые следует передать элементу <tr>. Они включают правильные ARIA-атрибуты и обработчики событий.

Пример использования:

import { useTableHeaderRow } from "@react-aria/table";
import { useTableState } from "@react-stately/table";

function TableHeader({ columns }) {
  let state = useTableState({ columns });
  let headerRow = state.collection.headerRows[0];
  let { rowProps } = useTableHeaderRow({ node: headerRow, state });

  return (
    <thead>
      <tr {...rowProps}>
        {headerRow.childNodes.map((column) => (
          <th key={column.key}>{column.rendered}</th>
        ))}
      </tr>
    </thead>
  );
}

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

  • Автоматическая поддержка ARIA roles: row, columnheader.
  • Встроенная обработка событий клавиатуры, если столбцы поддерживают сортировку или другие интерактивные действия.
  • rowProps гарантирует совместимость с технологией чтения с экрана и навигацией с клавиатуры.

useTableRow

useTableRow применяется для обычной строки таблицы (<tr>), обеспечивая правильное выделение, фокус и интеграцию с useTableSelection для выбора строк.

Синтаксис:

const { rowProps } = useTableRow({ node, state });
  • node — объект типа TableRowNode, описывающий текущую строку таблицы.
  • state — объект состояния таблицы (useTableState), который содержит информацию о выбранных строках, фокусе и сортировке.

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

  • rowProps — свойства для элемента <tr>, включая обработку фокуса и выделения.

Пример использования:

import { useTableRow } from "@react-aria/table";
import { useTableState } from "@react-stately/table";

function TableBody({ rows }) {
  let state = useTableState({ items: rows });

  return (
    <tbody>
      {state.collection.body.map((row) => {
        let { rowProps } = useTableRow({ node: row, state });
        return (
          <tr key={row.key} {...rowProps}>
            {row.childNodes.map((cell) => (
              <td key={cell.key}>{cell.rendered}</td>
            ))}
          </tr>
        );
      })}
    </tbody>
  );
}

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

  • Поддержка выбора строк через состояние state.selectionManager.
  • Автоматическая интеграция с фокусом (focusable, tabIndex) для клавиатурной навигации.
  • Генерация корректных ARIA-атрибутов для каждой строки: role="row", aria-selected, aria-rowindex.

Совместное использование useTableHeaderRow и useTableRow

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

  1. Создать состояние таблицы с помощью useTableState.
  2. Отрисовать заголовок таблицы через useTableHeaderRow.
  3. Отрисовать тело таблицы через useTableRow для каждой строки.
  4. Обеспечить выбор строк и взаимодействие с клавиатурой через state.selectionManager.

Пример полной таблицы:

import { useTableState } from "@react-stately/table";
import { useTableHeaderRow, useTableRow } from "@react-aria/table";

function Table({ columns, rows }) {
  let state = useTableState({ columns, items: rows });

  return (
    <table>
      <thead>
        {state.collection.headerRows.map((headerRow) => {
          let { rowProps } = useTableHeaderRow({ node: headerRow, state });
          return (
            <tr key={headerRow.key} {...rowProps}>
              {headerRow.childNodes.map((column) => (
                <th key={column.key}>{column.rendered}</th>
              ))}
            </tr>
          );
        })}
      </thead>
      <tbody>
        {state.collection.body.map((row) => {
          let { rowProps } = useTableRow({ node: row, state });
          return (
            <tr key={row.key} {...rowProps}>
              {row.childNodes.map((cell) => (
                <td key={cell.key}>{cell.rendered}</td>
              ))}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

Преимущества подхода:

  • Полная поддержка доступности для экранных читалок.
  • Простая интеграция выбора и сортировки.
  • Единый подход к генерации ARIA-атрибутов для строк и заголовков.
  • Возможность масштабирования таблиц с большим количеством данных.

Советы по использованию

  • Для таблиц с интерактивными элементами внутри заголовков (например, кнопки сортировки) использовать column.sortable и передавать обработчики через state.
  • В больших таблицах рекомендуется комбинировать с виртуализацией (react-virtual), при этом useTableRow корректно обновляет фокус при появлении новых строк.
  • Использовать rowProps и thProps/tdProps исключительно через деструктуризацию, чтобы не потерять встроенные обработчики событий и ARIA-атрибуты.

Этот подход обеспечивает полную совместимость с WAI-ARIA стандартами и делает таблицы в React максимально доступными и управляемыми с клавиатуры и экранных читалок.