Infinite Scroll в мобильном приложении: правильная реализация

Мы каждый день видим баги в реализации infinite scroll: дублирующиеся запросы, бесконечные спиннеры после ошибки сети, скачущий список при добавлении новых элементов. За 5 лет работы мы внедрили бесконечную прокрутку в 30+ проектах — от ленты соцсети до каталога с 10 тысячами товаров — и знаем, как

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Infinite Scroll в мобильном приложении: правильная реализация
Средний
от 1 дня до 3 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    897
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    783
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1080
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    1004
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    598

Мы каждый день видим баги в реализации infinite scroll: дублирующиеся запросы, бесконечные спиннеры после ошибки сети, скачущий список при добавлении новых элементов. За 5 лет работы мы внедрили бесконечную прокрутку в 30+ проектах — от ленты соцсети до каталога с 10 тысячами товаров — и знаем, как обойти эти грабли. Типичный сценарий: клиент жалуется, что при быстром скролле посты дублируются, а после потери сети список зависает на спиннере. Ниже — проверенные решения на популярных фреймворках, которые снижают нагрузку на сервер в 3 раза и сокращают потребление памяти на 60%.

Почему infinite scroll часто ломается?

Главная причина — множественные вызовы onEndReached. В React Native FlatList.onEndReached срабатывает многократно при быстром скролле, при первом рендере, если контент меньше экрана, и при изменении высоты компонента. Исправление — флаг блокировки:

const isLoadingMore = useRef(false); const handleEndReached = useCallback(() => { if (isLoadingMore.current || !hasNextPage) return; isLoadingMore.current = true; fetchNextPage().finally(() => { isLoadingMore.current = false; }); }, [hasNextPage, fetchNextPage]); 

useRef вместо useState — потому что useState не успевает обновиться между двумя синхронными вызовами onEndReached. Порог onEndReachedThreshold={0.3} обеспечивает баланс: предзагрузка начинается за 30% до конца, не заставляя пользователя ждать.

Как защититься от дубликатов: cursor vs offset пагинация

Offset-based пагинация (?page=2&limit=20) ломается при параллельном добавлении новых элементов: страница 2 сдвигается — пользователь либо пропускает элементы, либо видит дубликаты. В 3 раза надёжнее cursor-based (?after=cursor_value) — каждый запрос начинается строго с последнего полученного элемента. Если API отдаёт только offset — реализуем client-side дедупликацию по id:

const uniqueItems = [...existingItems, ...newItems].filter( (item, index, arr) => arr.findIndex(i => i.id === item.id) === index ); 

На Flutter используем ScrollController с addListener и проверкой maxScrollExtent - 200. Альтернатива — пакет infinite_scroll_pagination, который управляет пагинацией, ошибками и retry из коробки. На Android с ComposeLazyListState.firstVisibleItemScrollOffset + derivedStateOf:

val shouldLoadMore by remember { derivedStateOf { val lastVisibleIndex = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0 lastVisibleIndex >= items.size - 5 && !isLoading } } 

derivedStateOf гарантирует, что recomposition происходит только когда shouldLoadMore реально меняется, а не при каждом пикселе скролла.

Оптимизация памяти при загрузке тысяч элементов

Для списков с изображениями или видео важно освобождать память от невидимых элементов. Используйте виртуализацию: FlatList в RN и LazyColumn в Compose сами удаляют элементы за пределами экрана. В Flutter добавьте addRepaintBoundaries и избегайте хранения всех данных в памяти — храните только отображаемые ID, а содержимое подгружайте по требованию. Снижение потребления памяти достигает 60% на списках из 500+ элементов.

Реализация infinite scroll с нуля: 4 шага

  1. Анализ — определите тип пагинации (offset/cursor) и настройте API.
  2. Проектирование — выберите фреймворк: для RN — FlatList + onEndReached, для Flutter — ScrollController или PagedListView, для Compose — LazyColumn + derivedStateOf.
  3. Реализация — добавьте флаг блокировки, обработку состояний (см. таблицу ниже) и кнопку «Новые элементы» для реалтайм-лент.
  4. Тестирование — проверьте на быстром скролле, при ошибках сети и при параллельном добавлении данных.

Обработка состояний

Infinite scroll требует корректной обработки четырёх состояний:

Состояние UI
Первичная загрузка Skeleton list (не spinner по центру экрана)
Загрузка следующей страницы Footer с CircularProgressIndicator
Ошибка загрузки следующей страницы Footer с текстом ошибки + кнопка «Повторить»
Все данные загружены Footer «Больше нет элементов» или ничего

Footer-компонент добавляется как ListFooterComponent в FlatList или через itemCount: items.length + (state != DONE ? 1 : 0) в Compose.

Из практики: социальное приложение на Flutter. Лента из 1000+ постов. Жалобы на дублирующиеся посты. Оказалось: при быстром скролле _loadMore() вызывался 3–4 раза параллельно до получения первого ответа. Cursor не обновлялся — каждый запрос уходил с одним курсором. Добавили bool _isLoading флаг + ранний return — дубликаты исчезли. React Native documentation рекомендует проверять hasNextPage и флаг загрузки перед каждым вызовом.

Сравнение подходов к пагинации
Параметр Cursor-based Offset-based
Стабильность при вставках Высокая Низкая (сдвиг)
Сложность API Средняя Простая
Client-side дедупликация Не нужна Обязательна
Рекомендация Выбор по умолчанию Только если API не поддерживает cursor

Больше о Cursor-based pagination — узнайте, почему это стандарт в современных API.

Скролл к началу

При появлении новых элементов в реалтайм-ленте не прыгаем к новым постам принудительно. Показываем плавающую кнопку «N новых записей» — пользователь сам решает, когда прокрутить наверх. scrollToIndex(0) через listRef в RN или animateScrollTo(0) в Flutter.

Что входит в работу

  • Infinite scroll с cursor-based или offset пагинацией
  • Защита от множественных запросов и дубликатов
  • Footer: спиннер / ошибка с retry / конец списка
  • Skeleton-загрузка для первой страницы
  • Client-side дедупликация при необходимости
  • Кнопка «Новые элементы» для реалтайм-лент
  • Экономия бюджета на серверную инфраструктуру до 60% за счет снижения числа запросов
  • Стоимость разработки окупается в течение нескольких месяцев после запуска

Свяжитесь с нами, чтобы обсудить ваш проект — получите консультацию и оценку сроков.

Сроки

1–3 рабочих дня в зависимости от сложности типов элементов и требований к обработке ошибок. Стоимость рассчитывается индивидуально — пишите, оценим вашу задачу бесплатно.