React Aria предоставляет мощный набор хуков и утилит для создания доступных интерфейсов, в том числе для работы с таблицами и сортируемыми столбцами. Основным инструментом для реализации сортировки является хук useTable совместно с useTableColumn и useSortBy, которые позволяют управлять состоянием сортировки, предоставлять корректные ARIA-атрибуты и интегрироваться с кастомной логикой рендеринга.
useTable и
структура данныхДля работы с таблицей необходимо определить столбцы и данные. Столбцы описываются объектами с ключами:
name – отображаемое название столбцаkey – уникальный идентификатор столбцаsortable – флаг, определяющий, доступна ли
сортировкаПример структуры:
const columns = [
{ name: 'Имя', key: 'name', sortable: true },
{ name: 'Возраст', key: 'age', sortable: true },
{ name: 'Город', key: 'city', sortable: false }
];
const data = [
{ name: 'Алексей', age: 28, city: 'Москва' },
{ name: 'Мария', age: 34, city: 'Санкт-Петербург' },
{ name: 'Иван', age: 22, city: 'Казань' }
];
Хук useTable создаёт базовую структуру таблицы и возвращает методы для генерации ARIA-атрибутов и управления состоянием:
import { useTable } from '@react-aria/table';
const { tableProps, headerProps, rowProps } = useTable({ columns, data });
useSortByДля добавления сортировки используется хук useSortBy. Он принимает массив столбцов и текущее состояние сортировки, а возвращает обработчики для клика по заголовку и атрибуты для кнопок сортировки:
import { useSortBy } from '@react-aria/table';
const { sortDirection, sortButtonProps } = useSortBy({
columns,
sortState,
onSortChange: setSortState
});
Где:
sortDirection – направление сортировки для конкретного
столбца (ascending, descending или
none)sortButtonProps – ARIA-атрибуты для кнопки
заголовкаСостояние сортировки может храниться локально через React useState:
const [sortState, setSortState] = useState({ key: null, direction: null });
Заголовок столбца должен использовать свойства, возвращаемые
useSortBy, чтобы обеспечить доступность:
<th {...headerProps}>
<button {...sortButtonProps(column.key)}>
{column.name}
{sortDirection === 'ascending' ? ' ↑' : sortDirection === 'descending' ? ' ↓' : ''}
</button>
</th>
Ключевые моменты:
aria-sort,
aria-labelonSortChange↑ / ↓) помогает
пользователю понять текущее направление сортировкиПосле выбора столбца и направления необходимо отсортировать данные.
Обычно используется Array.prototype.sort:
const sortedData = [...data].sort((a, b) => {
const valueA = a[sortState.key];
const valueB = b[sortState.key];
if (valueA < valueB) return sortState.direction === 'ascending' ? -1 : 1;
if (valueA > valueB) return sortState.direction === 'ascending' ? 1 : -1;
return 0;
});
Особенности:
localeCompare для
корректного сравнения символовif (typeof valueA === 'string') {
return sortState.direction === 'ascending'
? valueA.localeCompare(valueB)
: valueB.localeCompare(valueA);
}
React Aria позволяет расширять сортировку до нескольких столбцов. Для этого хранится массив объектов состояния сортировки:
const [sortStates, setSortStates] = useState([
{ key: 'age', direction: 'ascending' },
{ key: 'name', direction: 'descending' }
]);
Функция сортировки проходит по массиву sortStates и
последовательно сравнивает значения:
sortedData.sort((a, b) => {
for (let { key, direction } of sortStates) {
if (a[key] < b[key]) return direction === 'ascending' ? -1 : 1;
if (a[key] > b[key]) return direction === 'ascending' ? 1 : -1;
}
return 0;
});
React Aria не ограничивает использование стандартных HTML-таблиц. Можно создавать кастомные компоненты для ячеек и заголовков, сохраняя доступность:
function SortableHeader({ column }) {
const { sortDirection, sortButtonProps } = useSortBy({ columns, sortState, onSortChange: setSortState });
return (
<div role="columnheader" {...sortButtonProps(column.key)}>
{column.name}
{sortDirection === 'ascending' ? ' ↑' : sortDirection === 'descending' ? ' ↓' : ''}
</div>
);
}
Преимущества такого подхода:
Сортируемые столбцы в React Aria автоматически получают поддержку клавиатуры:
aria-sort сигнализируют о текущем
состоянииДля полного контроля можно комбинировать useTable и useFocusRing, чтобы визуально выделять активный элемент при навигации с клавиатуры.
Если данные приходят асинхронно, состояние сортировки можно применять после загрузки:
useEffect(() => {
if (data.length > 0 && sortState.key) {
setSortedData(sortData(data, sortState));
}
}, [data, sortState]);
Это позволяет поддерживать сортировку даже при изменении данных на сервере или при фильтрации.
key для столбцов, чтобы React
корректно обновлял DOMreact-virtual или react-windowСоблюдение этих принципов обеспечивает корректную, доступную и производительную реализацию сортируемых таблиц в React Aria.