Как оптимизировать рендеринг большого списка в React?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Оптимизация рендеринга больших списков в React
Рендеринг больших списков — частая проблема в фронтенде. Если нерационально подойти, пользователь видит лаги, фризы и плохой UX. Есть несколько проверенных техник для решения этой проблемы.
Проблема: почему падает производительность
Когда список содержит 1000+ элементов, React должен рендерить каждый элемент в DOM. Это требует:
- Парсинга JSX каждого элемента
- Создания компонентов в памяти
- Добавления в DOM-дерево
- Переразметки и переотрисовки браузером
Результат: падает FPS, браузер зависает.
Решение 1: Виртуализация (Virtual Scrolling)
Идея простая: рендерим только видимые элементы + буфер за границами экрана.
import { FixedSizeList } from 'react-window';
function LargeList({ items }) {
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style} className="item">
{items[index].name}
</div>
)}
</FixedSizeList>
);
}
Плюсы: Работает даже с 100,000 элементами, минимальная память. Минусы: Требует фиксированной высоты элемента, сложнее с динамическим контентом.
Решение 2: Pагинация
Загружаем элементы порциями, показываем кнопку "Загрузить еще" или автоматическую подгрузку при скролле.
function PaginatedList() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const loadMore = async () => {
setIsLoading(true);
const newItems = await fetchItems(page, limit: 50);
setItems(prev => [...prev, ...newItems]);
setPage(page + 1);
setIsLoading(false);
};
return (
<div>
{items.map(item => <div key={item.id}>{item.name}</div>)}
<button onClick={loadMore} disabled={isLoading}>
{isLoading ? 'Загрузка...' : 'Загрузить еще'}
</button>
</div>
);
}
Плюсы: Просто реализуется, работает с динамической высотой. Минусы: Пользователь должен скроллить много, неудобно для поиска по всем данным.
Решение 3: Мемоизация компонентов
Используем React.memo и useMemo для предотвращения ненужных ре-рендеров.
const ListItem = React.memo(({ item, onSelect }) => {
return (
<div onClick={() => onSelect(item.id)}>
{item.name}
</div>
);
});
function ListWithMemo({ items, onSelect }) {
const memoizedOnSelect = useCallback((id) => {
onSelect(id);
}, [onSelect]);
return (
<div>
{items.map(item => (
<ListItem
key={item.id}
item={item}
onSelect={memoizedOnSelect}
/>
))}
</div>
);
}
Плюсы: Простое добавление, работает с любым списком. Минусы: Не поможет, если реально много элементов (100+), нужна комбинация методов.
Мой рекомендуемый подход
Комбинирую методы в зависимости от сценария:
- 100-500 элементов: Мемоизация + фильтрация на клиенте
- 500-5000: Пагинация + мемоизация
- 5000+: Виртуализация (react-window или react-virtualized)
Для максимальной производительности всегда профилирую с React DevTools Profiler.