Производительность больших списков

Работа с большими коллекциями элементов в веб-приложениях часто сталкивается с проблемами производительности. При прямом рендеринге всех элементов списка DOM быстро разрастается, что приводит к торможению интерфейса и задержкам в отклике пользователя. React Aria предоставляет инструменты для управления такими сценариями, обеспечивая доступность и оптимизацию рендеринга.


Использование виртуализации для списков

Для больших списков ключевым подходом является виртуализация — рендеринг только видимых элементов на экране. React Aria не предоставляет встроенный компонент виртуализации, но тесно интегрируется с библиотеками вроде react-virtual или react-window. Основная идея заключается в следующем:

  1. Определяется видимая область (viewport) списка.
  2. Рассчитывается диапазон индексов элементов, которые должны быть отрисованы.
  3. Остальные элементы не рендерятся в DOM, что существенно снижает нагрузку.

Пример интеграции с react-virtual:

import { useListState } from '@react-stately/list';
import { useListBox, useOption } from '@react-aria/listbox';
import { useVirtualizer } from '@tanstack/react-virtual';

function VirtualList({ items }) {
  const state = useListState({ items });
  const parentRef = React.useRef();

  const rowVirtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 50, // высота одного элемента
  });

  return (
    <div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
      <div style={{ height: rowVirtualizer.getTotalSize(), position: 'relative' }}>
        {rowVirtualizer.getVirtualItems().map(virtualRow => {
          const item = items[virtualRow.index];
          return (
            <ListItem
              key={item.key}
              item={item}
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                transform: `translateY(${virtualRow.start}px)`
              }}
            />
          );
        })}
      </div>
    </div>
  );
}

function ListItem({ item, style }) {
  const ref = React.useRef();
  const { optionProps, isSelected } = useOption({ key: item.key }, item, null);

  return (
    <div {...optionProps} ref={ref} style={style}>
      {item.name}
      {isSelected && ' (выбрано)'}
    </div>
  );
}

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

  • Использование useListState и useOption сохраняет доступность списка для клавиатуры и скринридеров.
  • useVirtualizer вычисляет положение и видимость элементов без рендеринга всего списка.

Минимизация повторных рендеров

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

  • Мемоизация элементов: React.memo позволяет предотвратить повторный рендер элементов списка, если их пропсы не изменились.
  • Селекторы состояния: при использовании useListState можно извлекать только нужную часть состояния для конкретного элемента, чтобы изменение другой части списка не вызывало перерисовку.
  • Контекстное разделение: вместо передачи всего состояния через контекст, создаются специализированные контексты для отдельных подсистем (например, выделение, фокус), что снижает количество подписчиков на изменения.

Ленивая загрузка и пагинация

Для экстремально больших списков стоит комбинировать виртуализацию с ленивой загрузкой (lazy loading):

  1. Разбить данные на страницы.
  2. Загружать только текущую и соседние страницы.
  3. При скролле автоматически догружать новые страницы и обновлять виртуальный список.

React Aria позволяет сохранить полную доступность даже при динамической загрузке элементов, так как состояние списка управляется через useListState.


Обработка динамических размеров элементов

В реальных приложениях элементы списка могут иметь разную высоту. В этом случае фиксированная виртуализация становится неэффективной. Подход:

  • Использовать estimateSize с усреднённой высотой и корректировать позицию через getItemSize.
  • Поддерживать реальный размер элемента через рефы, чтобы виртуализатор мог корректно позиционировать элементы.
estimateSize: (index) => measuredSizes[index] ?? 50

Таким образом, даже сложные макеты с переменной высотой остаются отзывчивыми.


Управление фокусом и клавиатурная навигация

React Aria обеспечивает полностью доступные списки, что критично при виртуализации:

  • Использование useListBox и useOption автоматически обрабатывает фокусировку.
  • Виртуализация не ломает навигацию стрелками, так как элементы, даже если они не отрисованы, учитываются в состоянии useListState.
  • Поддержка aria-activedescendant позволяет навигации работать через виртуализированный DOM.

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

  1. Виртуализировать все списки более 50–100 элементов.
  2. Использовать мемоизацию компонентов списка (React.memo).
  3. Разделять контексты состояния по зонам ответственности.
  4. Реализовать ленивую загрузку при больших объёмах данных.
  5. Поддерживать корректное управление фокусом через React Aria для доступности.
  6. При переменной высоте элементов использовать измерение реального размера и динамическое позиционирование.

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