Как оптимизировать React приложение, если оно тормозит при большом количестве элементов?

У меня есть список с несколькими тысячами товаров, и при скроллинге приложение начинает сильно лагать. Пробовал обычную virtualisation, но помогает не очень. Может, есть какой-то современный подход, который я упускаю?

5 ответов

★ Лучший ответ

А у тебя вообще профилировал приложение, или гадаешь на кофейной гуще? Потому что если виртуализация не помогает, то скорее всего лаги идут из-за того, что ты перерендериваешь весь список целиком при каждом изменении стейта - даже если видимых элементов всего 20, React может пересчитывать данные для всех 5000. Попробуй обернуть компоненты строк в `React.memo` и убедись, что ты не создаёшь новые функции-обработчики на каждый рендер (используй `useCallback`).

Первое - забудь про обычную виртуализацию, если она не помогает, значит проблема скорее всего не в рендере, а в том, что тащишь слишком много данных сразу или вычисляешь их заново при каждом скролле. Проверь, правильно ли ты используешь `memo`, `useMemo` и `useCallback` - часто бывает, что компоненты пересчитываются по пустякам. Ещё полезно профилировать приложение через React DevTools Profiler, чтобы увидеть, где именно затык: может быть, дело в самом рендере списка, а может быть, в том, что при каждой прокрутке ты гонишь запросы на сервер или пересчитываешь сложную логику.

Если виртуализация уже используется, попробуй увеличить размер окна рендеринга (overscan) или переходи на пагинацию вместо бесконечного скролла - иногда это просто проще и быстрее.

Присмотрись к мемоизации селекторов и функций обратного вызова - часто лаги идут не от самого рендера, а от того, что при каждом скролле пересчитываются фильтры, сортировка или создаются новые функции для обработчиков! Попробуй обернуть селекторы в `useSelector` с правильным сравнением или используй `useMemo` для вычислений - это может дать намного больший прирост производительности, чем просто виртуализация.

Проверь, сколько data-атрибутов и классов ты вешаешь на каждый элемент списка. Часто лаги идут не от количества DOM-узлов, а от того, что браузер постоянно пересчитывает стили и макет из-за изменений классов при скролле. Если ты динамически добавляешь классы для активного элемента или наведения, это может стать узким местом. Попробуй переместить это в CSS с использованием `nth-child` или отдельного слоя через `position: fixed`, чтобы браузер не трогал основной список.

Второй момент - убедись, что ты не создаёшь новые объекты в пропсах при каждом рендере. Например, если передаёшь `style={{ color: item.color }}` вместо предвычисленного значения, React будет видеть разные объекты и пересчитывать компонент. Даже с мемоизацией это может замедлить. Лучше подготовить стили заранее или использовать CSS-переменные.

И ещё: если в списке тысячи элементов, даже виртуализация может быть медленной, если ты создаёшь слишком много DOM-узлов в окне видимости одновременно. Попробуй уменьшить количество отрисовываемых строк за раз - показывай только видимую часть плюс небольшой буфер выше и ниже. В большинстве библиотек виртуализации это настраивается через параметр вроде `overscanRowCount`.

На самом деле виртуализация обычно работает отлично, если реально помогает с рендером - значит, откуда-то ещё идут лаги. Одна частая беда, которую упускают: у тебя может быть проблема с бесконечным скроллом и загрузкой данных. Если при каждом скролле ты запрашиваешь новую порцию товаров с бэка и не кэшируешь результаты, браузер лагает не от DOM-узлов, а от задержек сети и парсинга JSON.

Второе, что стоит проверить - правильно ли ты настроил сами размеры виртуализации. Часто люди ставят слишком маленькую высоту элемента в конфиге, и библиотека вроде react-window начинает пересчитывать видимые элементы чуть ли не на каждый пиксель скролла. Попробуй увеличить буфер и явно указать высоту строки, а не полагаться на авторасчёт.

Ваш ответ

Войдите, чтобы ответить.