Sobes.tech
Назад к вопросам
Middle
8

Реализовать виртуальный скролл: есть длинный список сообщений (10 000 элементов), нужно рендерить только видимые элементы внутри контейнера с фиксированной высотой 300px, высота одной строки — 30px.

Компании, где спрашивали
Basis

Ответ от нейросети

sobes.tech AI

Для реализации виртуального скролла с 10 000 элементами и фиксированной высотой контейнера 300px (высота строки 30px) можно использовать следующий подход:

  1. Вычислить количество видимых элементов: 300px / 30px = 10 элементов.
  2. Отслеживать текущий скролл контейнера и вычислять индекс первого видимого элемента: startIndex = Math.floor(scrollTop / 30).
  3. Рендерить только элементы с индексами от startIndex до startIndex + visibleCount + buffer (buffer — небольшой запас, например 2 элемента, чтобы избежать мерцания).
  4. Для сохранения общей высоты списка использовать пустой блок с высотой totalHeight = 10000 * 30 = 300000px.
  5. Сдвигать контейнер с видимыми элементами вниз на startIndex * 30px с помощью CSS transform или padding-top.

Пример на React:

function VirtualList({ items }) {
  const containerHeight = 300;
  const rowHeight = 30;
  const totalCount = items.length;
  const visibleCount = Math.ceil(containerHeight / rowHeight);
  const buffer = 2;

  const [scrollTop, setScrollTop] = React.useState(0);

  const startIndex = Math.floor(scrollTop / rowHeight);
  const endIndex = Math.min(startIndex + visibleCount + buffer, totalCount);

  const visibleItems = items.slice(startIndex, endIndex);

  const onScroll = (e) => {
    setScrollTop(e.currentTarget.scrollTop);
  };

  return (
    <div style={{ height: containerHeight, overflowY: 'auto' }} onScroll={onScroll}>
      <div style={{ height: totalCount * rowHeight, position: 'relative' }}>
        <div style={{
          position: 'absolute',
          top: startIndex * rowHeight,
          left: 0,
          right: 0
        }}>
          {visibleItems.map((item, index) => (
            <div key={startIndex + index} style={{ height: rowHeight }}>
              {item}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

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