Головна / Статті / Як React Query скоротив 500 рядків у шарі API додатку React Native

Як React Query скоротив 500 рядків у шарі API додатку React Native

Реальний кейс використання React Native, який демонструє, як перехід від ручного отримання даних за допомогою useEffect до React Query усунув повторюваний код та покращив функції кешування та роботу в офлайн-режимі.

1364 слів

Вступ

Деякий час тому кодова база React Native, яку ви можете впізнати, зіткнулася зі знайомою проблемою.

Майже кожен екран, який потребував віддалених даних, дотримувався однакової структури:

  • Запити на отримання даних, розміщені всередині useEffect
  • Кілька окремих позначок завантаження
  • Власні блоки обробки помилок
  • Логіка оновлення даних шляхом прокрутки, реалізована вручну
  • Механізми повторної спроби вручну
  • Тимчасові рішення щодо локального кешування

З функціональної точки зору ніщо з цього не було несправним. Але підтримка єдності цієї структури на десятках екранів перетворилася на справжню тягар для обслуговування.

Перехід на React Query (від TanStack) дозволив команді позбутися великої кількості повторюваного коду для роботи з мережею, отримавши взамін кращу систему кешування, більш ефективне керування процесом завантаження та надійнішу роботу в офлайн-режимі. Ця бібліотека містить вбудоване кешування запитів, автоматичне оновлення даних у фоновому режимі та логіку, яка враховує особливості мережі, що значно зменшує потребу у власному написанні механізмів керування станом.

Далі наведено порівняння старого ручного підходу та версії з React Query, засноване на прикладах реальних екранів React Native у продакшені.

Проблеми традиційного керування API

Налаштування типового екрана виглядало приблизно так:

const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [error, setError] = useState(null);

useEffect(() => {
  fetchData();
}, []);
const fetchData = async () => {
  try {
    setLoading(true);
    const response = await api.getPosts();
    setData(response);
  } catch (err) {
    setError(err);
  } finally {
    setLoading(false);
  }
};

Цей же шаблон постійно зустрічався в усьому додатку.

Кожен екран мусив самостійно керувати:

  • Флагом завантаження
  • Флагом помилки
  • Механізмом повторної спроби
  • Логіка оновлення шляхом потягування
  • Власний підхід до кешування
  • Ручне запускання повторного завантаження
  • У міру зростання додатку ця повторювана схема ставала все складнішою для збереження послідовності.

    З’являється React Query

    Той самий екран, переписаний за допомогою React Query, складається з наступного:

    const { data, isLoading, error, refetch } = useQuery({
      queryKey: ["posts"],
      queryFn: fetchPosts,
    });
    

    Це і є вся реалізація.

    React Query автоматично керує усім наступним:

    • Індикатори завантаження
    • Стани помилок
    • Усунення дублікатів однакових запитів
    • Повторне завантаження даних у фоновому режимі
    • Керування кешем
    • Повторна спроба виконання невдалих запитів
    • Реагування на відновлення з’єднання мережі

    Більшість цих функцій доступні без додаткових налаштувань, а їх можна налаштовувати за допомогою параметрів, таких як staleTime, gcTime, налаштування повторних спроб та параметри повторного завантаження.

    Порівняння №1: Запити до мережі

    До React Query

    Profile Screen → API Call
    Settings Screen → API Call
    Dashboard Screen → API Call
    

    Результат: три окремі запити до мережі за однаковими даними.

    З React Query

    Profile Screen → API Call
    Settings Screen → Cached Data
    Dashboard Screen → Cached Data
    

    Цього разу результат: лише один запит до мережі.

    Результат у продакшені

    • Набагато меншої кількості дублікатних запитів до API
    • Нижчого навантаження на сервери бекенду
    • Швидших переходів між екранами

    Порівняння №2: Ефективність кешування

    Можна сказати, що саме у сфері кешування React Query принесає найбільшу користь.

    Коли та сама запитувана інформація знову запитується до того, як її кешована копія стане застарілою:

    useQuery({
      queryKey: ["products"],
      queryFn: getProducts,
      staleTime: 300000,
    });
    

    Інтерфейс може негайно відображати кешовані дані, причому React Query за бажанням тихо оновлює їх на фоні. Кешовані записи зберігаються та зрештою видаляються згідно з налаштуваннями, якими ви керуєте.

    Реальний приклад

    Розгляньмо екран списку товарів у електронному магазині.

    Без кешування відкриття цього екрана означає:

    Open Products
    ↓
    Network Request
    ↓
    Navigate Back
    ↓
    Open Products Again
    ↓
    Network Request
    

    З використанням React Query той самий процес виглядає так:

    Open Products
    ↓
    Network Request
    ↓
    Navigate Back
    ↓
    Open Products Again
    ↓
    Instant Cached Data
    

    Різниця полягає у тому, що додаток здається значно швидшим для користувача.

    Порівняння №3: Стани завантаження

    До впровадження React Query відстеження стану завантаження означало необхідність керування кількома булевими значеннями:

    const [loading, setLoading] = useState(false);
    const [refreshing, setRefreshing] = useState(false);
    const [isRetrying, setIsRetrying] = useState(false);
    

    Після цього один виклик хука розкриває все необхідне:

    const {
      isLoading,
      isFetching,
      isRefetching,
    } = useQuery(...)
    

    React Query розрізняє саме перше завантаження та будь-які наступні фонові запити, що значно спрощує розуміння логіки користувацького інтерфейсу, пов’язаної з ними.

    Справжня перевага

    Замість того, щоб показувати спінер на всьому екрані під час кожного запиту, додаток може розрізняти:

    • Початкове завантаження: спінер на всьому екрані
    • Фонове оновлення: невеликий, непомітний спінер
    • Дані, які вже збережені у кеші: жодних видимих перерв

    Це робить додаток значно відповідальнішим щодо користувача.

    Порівняння №4: підтримка офлайн-режиму

    Поведінка додатку в офлайн-режимі — це одна з тих речей, які команди часто недооцінюють.

    Робота з нею вручну зазвичай виглядає так:

    Check Connectivity
    Pause Requests
    Retry Later
    Handle Errors
    Refetch On Reconnect
    

    Це означає наявність спеціальної логіки підключення, розкиданої по всьому кодовому базі.

    React Query пропонує вбудоване керування роботою в мережі та поза нею, а також функцію повторного завантаження даних, яка реагує на події відновлення з’єднання. У React Native можна налаштувати це за допомогою onlineManager разом із прослуховувачами статусу мережі платформи.

    Наприклад:

    onlineManager.setEventListener(...)
    

    React Query також можна налаштувати для роботи у режимі «спочатку офлайн», що змінює його поведінку в мережі відповідно.

    Реальний приклад

    Візьмемо за приклад додаток для читання новин.

    Протягом дня статус з’єднання користувача може виглядати так:

    • Ранок: онлайн
    • Після обіду: офлайн
    • Вечір: знову онлайн

    Протягом усього цього періоду раніше збережені статті залишаються доступними для читання, а як тільки з’єднання відновлюється, новий контент може автоматично синхронізуватися.

    Практичні сценарії використання

    1. Додатки новин

    Що це дає:

    • Статті у кеші
    • Автоматичне оновлення фону
    • Зниження загального трафіку API
    • Можливість продовжувати читання в офлайн-режимі

    2. Додатки електронної комерції

    Що це дає:

    • Списки товарів у кеші
    • Заздалегідь завантаження категорій
    • Швидше навігування між розділами
    • Значно плавніший досвід шопінгу

    React Query також підтримує завантаження даних заздалегідь ще до початку навігації, що скорочує відчутний час очікування.

    3. Додатки панелей керування

    Що це дає:

    • Віджети, які автоматично оновлюються
    • Кеш, який використовується на кількох екранах
    • Зменшення кількості мережевих операцій
    • Набагато простіше управління станом в цілому

    Ця схема особливо добре підходить для аналітичних панелей та панелей керування адміністраторами.

    Що ми насправді видалили

    Як тільки міграція була завершена:

    Видалено

    • Самостійна реалізація обробки стану завантаження
    • Ручний код для повторної спроби
    • Дублювання вихідних запитів API
    • Шаблон для оновлення даних шляхом прокрутки
    • Власна логіка кешування
    • Ручне керування повторним отриманням даних

    Додано

    • Сама бібліотека React Query
    • Ключі запитів
    • Екземпляр QueryClient

    Кінцевий результат: приблизно 500 рядків коду для обробки API більше не були потрібні.

    Коли React Query може бути зайвим

    Використання React Query може бути зайвим, якщо:

    • Ваш додаток робить лише кілька запитів до API
    • Основні дані майже не змінюються
    • Кешування справді не потрібне
    • Робота в офлайн-режимі не має значення для вашого випадку використання

    Проте для більшості продакшн-додатків переваги зазвичай досить швидко переважають над початковою складністю навчання.

    Заключні міркування

    React Query — це не просто ще один спосіб отримання даних.

    Він є повноцінним рішенням для керування станом сервера, яке усуває повторюваний код API та водночас покращує кешування, поведінку завантаження, ефективність мережі та роботу в офлайн-режимі.

    Найбільшою вигодою тут була не просто висока продуктивність.

    Це було зменшення складності.

    Менше коду. Комплексніших помилок. Кращий досвід для користувачів додатку.

    Саме ця комбінація зробила міграцію вартою.