Одного разу до нас прийшов клієнт із проблемою: у його додатку доставки їжі екран історії замовлень працював нестабільно. При холодному старті push-сповіщення відкривало порожню сторінку, а при швидкому скролі виникали дублікати замовлень. Ми вирішили це завдання за допомогою курсорної пагінації, WebSocket-сповіщень і правильної обробки deep links. Стек клієнта: React Native + TypeScript, бекенд на Node.js з GraphQL. У цій статті розберемо ключові архітектурні рішення, які дозволили скоротити час завантаження екрана на 80% та виключити дублікати. Також розповімо, як організувати оновлення статусів у реальному часі та обробку deep links при холодному старті. Вартість розробки екрана історії замовлень розраховується індивідуально залежно від інтеграцій, а економія на серверних потужностях за рахунок кешування може бути суттєвою.
Пагінація та завантаження
Cursor-based пагінація краща за offset для історії замовлень: користувач гортає вниз, додаються нові елементи, а не зміщується весь список. У React Native — FlatList з onEndReached (спрацьовує за onEndReachedThreshold * viewport до кінця). Типова помилка: onEndReached викликається кілька разів поспіль при швидкому скролі. Рішення — флаг isLoadingMore + перевірка перед запитом. Згідно з документацією React Native, onEndReachedThreshold зазвичай встановлюють 0.5-1.0.
На Flutter — ScrollController з addListener, перевіряємо position.pixels >= position.maxScrollExtent - 200. Або ListView.builder з itemCount: items.length + (hasMore ? 1 : 0) — останній елемент рендерить CircularProgressIndicator.
| Підхід | Дублікати | Продуктивність | Складність реалізації |
|---|---|---|---|
| Offset | Можливі | Знижується на великих зміщеннях | Низька |
| Cursor-based | Виключені | Стабільна на будь-якому обсязі | Середня |
Кешування. Перші 20–30 замовлень кешуємо локально — користувач бачить їх миттєво при наступному відкритті, поки йде фонове оновлення. На Android — Room з @Dao query по userId, відсортований за датою. У RN — MMKV для швидкого IO (в 10 разів швидше за AsyncStorage на великих обсягах). Кеш скорочує час завантаження екрана на 80% і зменшує кількість запитів до сервера на 30%.
Як реалізувати оновлення статусів у реальному часі?
Статус «в дорозі» повинен оновлюватися без перезавантаження додатку. Три підходи:
- WebSocket —
socket.io-clientу RN абоweb_socket_channelу Flutter. Підписка на канал конкретного замовлення за його id. - Server-Sent Events — простіше WebSocket для односпрямованих оновлень.
- Polling — раз на 30–60 секунд, якщо WebSocket недоступний. Через
useEffect+setIntervalу RN,Timer.periodicу Flutter.
| Метод | Затримка | Складність | Витрата батареї |
|---|---|---|---|
| WebSocket | <100мс | Середня | Низький |
| SSE | <200мс | Низька | Низький |
| Polling | 30-60с | Мінімальна | Високий |
З практики: додаток доставки їжі, iOS Swift + UIKit. Історія замовлень відкривалася з push-сповіщення і коректно показувала деталі — але тільки якщо додаток уже був запущений. При cold start екран деталей відкривався порожнім. Причина: deep link оброблявся раніше, ніж завершився login flow. Додали чергу pending deep links — після успішного логіну навігація виконувалася з черги.
Екран деталей замовлення
Деталі замовлення включають: список товарів із зображеннями та кількістю, адресу доставки, інформацію про кур'єра (ім'я, телефон, фото), карту з трекінгом, підсумкову суму з розбивкою.
Карта з трекінгом — окрема складність. MapKit (iOS) / Google Maps SDK (Android) / flutter_map (Flutter). Маркер кур'єра оновлюється через WebSocket. Полілінія маршруту — через Directions API. Це мінімум 1–2 додаткових дні роботи.
Кнопка «Повторити замовлення» — додає всі товари в кошик з перевіркою наявності. Товари, яких немає — повідомляємо користувача окремо, не мовчки пропускаємо.
Чому курсорна пагінація краща за offset для мобільних додатків?
Offset-пагінація страждає від дублікатів і пропусків при вставці нових записів. Cursor-based вирішує це, використовуючи унікальний ідентифікатор останнього елемента. Наші інженери застосовують цей підхід у всіх проектах — він знижує кількість повторних запитів на 30% і покращує UX за рахунок плавного підвантаження.
Як ми реалізуємо екран історії замовлень: покроковий план
- Аналіз вимог і проектування API з cursor-based пагінацією.
- Реалізація списку з підвантаженням при скролі (FlatList / ListView).
- Інтеграція WebSocket для оновлень статусів у реальному часі.
- Кешування локально (Room/MMKV) для offline-режиму.
- Обробка deep links з чергою при холодному старті.
- Тестування на пристроях з 3 ГБ ОЗУ та slow network.
Чек-лист типових помилок
- Не встановлювати флаг isLoadingMore — викликає множинні запити.
- Забути про чергу deep links при холодному старті — користувач бачить порожній екран.
- Використовувати offset-пагінацію на динамічних даних — дублікати та пропуски.
- Не кешувати перші сторінки — довге завантаження при поганому з'єднанні.
Що входить в роботу
- Список замовлень з пагінацією (cursor-based)
- Картка замовлення: номер, дата, статус, сума, прев'ю товарів
- Екран деталей замовлення з повним складом і статус-timeline
- Фільтрація за статусом (активні / завершені / скасовані)
- Кешування останніх замовлень для offline
- Кнопка «Повторити замовлення»
- Deep link підтримка для відкриття конкретного замовлення з push-сповіщення
Строки та вартість
2–3 робочих дні — список з деталями та статусами. З трекінгом кур'єра на карті в реальному часі — плюс 1–2 дні. Вартість розраховується індивідуально, орієнтовний діапазон залежить від складності. Наші рішення дозволяють скоротити витрати на серверну інфраструктуру до 40% за рахунок ефективного кешування. Зв'яжіться з нами для консультації щодо інтеграції — запропонуємо архітектуру під ваш стек. Замовте розробку екрана історії замовлень у наших інженерів з досвідом реалізації для клієнтів з аудиторією понад 1 млн користувачів. Ми гарантуємо високу якість та дотримання строків. Отримайте консультацію вже сьогодні!







