Strona główna / Artykuły / Jak React Query skrócił 500 linii w warstwie API aplikacji React Native

Jak React Query skrócił 500 linii w warstwie API aplikacji React Native

Przykład z rzeczywistego świata dotyczący React Native, który pokazuje, jak przejście z ręcznego pobierania danych za pomocą useEffect na React Query pozwoliło usunąć powtarzający się kod oraz poprawić funkcjonowanie cache’owania i obsługę pracy offline.

1364 słów

Wprowadzenie

Jakieś czas temu kodbase React Native, który może być panu znany, napotkał podobny problem.

Prawie każdy ekran wymagający danych zdalnych stosował ten sam schemat:

  • Zawieranie wywołań do pobierania danych wewnątrz useEffect
  • Kilka oddzielnych flag oznaczających stan ładowania
  • Sprawdzone bloki obsługi błędów
  • Ręcznie napisana logika odświeżania treści
  • Mechanizmy ręcznego ponawiania prób
  • Tymczasowe rozwiązania dotyczące lokalnego przechowywania danych

Funkcjonalnie nic z tego nie było uszkodzone. Jednak utrzymanie spójności w dziesiątkach ekranów stało się prawdziwym obciążeniem pod względem konserwacji.

Przejście na React Query (od TanStack) pozwoliło zespołowi usunąć dużą ilość powtarzalnego kodu sieciowego, uzyskując w zamian lepsze zarządzanie pamięcią cache, bardziej przejrzyste sterowanie procesem ładowania oraz bardziej niezawodne działanie w trybie offline. Biblioteka oferuje wbudowane cacheowanie zapytań, automatyczne ponowne pobieranie danych w tle oraz logikę uwzględniającą warunki sieciowe, co eliminuje potrzebę ręcznego pisania takiej mechaniki zarządzania stanem.

Poniżej przedstawiono porównanie starego, ręcznego podejścia z wersją wykorzystującą React Query, na podstawie wzorów z rzeczywistych ekranów w React Native używanych w produkcji.

Problemy z tradycyjnym zarządzaniem API

Konfiguracja typowego ekranu wyglądała mniej więcej w ten sposób:

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);
  }
};

To samo szablon pojawiał się wielokrotnie w całym aplikacji.

Każdy ekran musiał samodzielnie zarządzać:

  • Flagą ładowania
  • Flagą błędu
  • Obsługą ponownych prób
  • Logika odświeżania przy przeciągnięciu
  • Swoje własne podejście do cacheowania
  • Ręczne uruchamianie ponownego pobierania danych
  • W miarę rozwoju aplikacji to powtarzające się wzorce stawały się coraz trudniejsze do utrzymania w spójności.

    Pojawia się React Query

    To samo interfejs, przepisane przy użyciu React Query, sprowadza się do tego:

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

    To jest cała implementacja.

    React Query automatycznie zajmuje się wszystkim poniższym:

    • Wskazówki ładowania
    • Stany błędów
    • Usuwanie identycznych zapytań w trakcie przetwarzania
    • Ponowne pobieranie danych w tle
    • Zarządzanie cache’em
    • Ponawianie nieudanych zapytań
    • Reagowanie na ponowne połączenie sieciowe

    Większość tych funkcji jest dostępna od razu, a ich ustawienia można dostosować za pomocą parametrów takich jak staleTime, gcTime, konfiguracja ponawiania prób oraz ustawienia odświeżania danych.

    Porównanie nr 1: Zapytania sieciowe

    Przed React Query

    Załóżmy trzy oddzielne ekrany, które wszystkie potrzebują tych samych danych profilu użytkownika.

    Bез żadnej wspólnej warstwy cache’owania, każdy z nich wysyła własne zapytanie:

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

    Wynik: trzy oddzielne żądania sieciowe do tych samych danych.

    Z React Query

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

    Tym razem wynik: tylko jedno żądanie sieciowe.

    React Query przechowuje wyniki pod kluczem zapytania i udostępnia te dane z cache’u wszystkim komponentom, które o nie proszą. Każdy ekran, który później poprosi o ten sam klucz, otrzymuje wartość z cache’u natychmiast, a tło może w tle aktualizować te dane.

    Wynik w produkcji

    Na ekranach, które użytkownicy często odwiedzają, oznacza to:

    • Znacznie mniej duplikatowych żądań do API
    • Mniejsze obciążenie serwerów backendowych
    • Szybsze przejścia między ekranami

    Porównanie #2: Efektywność buforowania

    Buferowanie to chyba obszar, w którym React Query przynosi największe korzyści.

    Gdy ta sama zapytanie jest ponownie wysyłane, zanim jego zbuforowana kopia stanie się przestarzała:

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

    Interfejs może natychmiast pokazać dane z bufora, przy czym React Query opcjonalnie odświeża je cicho w tle. Zapisy z bufora są przechowywane i ostatecznie usuwane według ustawień, które sami określamy.

    Rzeczywisty przykład

    Rozważmy ekran listy produktów w aplikacji e-commerce.

    Bez buforowania otwarcie tego ekranu oznacza:

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

    Z użyciem React Query ten sam proces wygląda tak:

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

    Różnica polega na tym, że aplikacja wydaje się znacznie szybsza dla osoby, która ją używa.

    Porównanie #3: Stany ładowania

    Zanim zaczęto używać React Query, śledzenie stanu ładowania wymagało obsługi kilku wartości logicznych:

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

    Następnie jedna wywołanie hooka ujawnia wszystko, co jest potrzebne:

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

    React Query rozróżnia pierwsze załadowanie od wszelkich późniejszych żądań w tle, co znacznie upraszcza logikę interfejsu użytkownika związaną z nimi.

    Prawdziwa korzyść

    Zamiast pokazywać spinner na całym ekranie przy każdym żądaniu, aplikacja może odróżniać:

    • Pierwotne załadowanie: spinner na całym ekranie
    • Odświeżenie w tle: mały, niewidoczny spinner
    • Dane już zlokalizowane w pamięci: brak żadnej widocznej przerwy

    Dzięki temu aplikacja wydaje się znacznie bardziej responsywna dla użytkownika.

    Porównanie nr 4: Obsługa offline

    Zachowanie aplikacji w trybie offline to jedna z kwestii, które zespoły mają tendencję do lekceważenia.

    Ręczne radzenie sobie z tym zwykle wygląda w ten sposób:

    Check Connectivity
    Pause Requests
    Retry Later
    Handle Errors
    Refetch On Reconnect
    

    To oznacza konieczność stosowania specjalistycznej logiki łączności rozrzuconej po całym kodzie źródłowym.

    React Query oferuje natywną obsługę pracy online i offline, wraz z funkcją ponownego pobierania danych reagującą na wydarzenia ponownego połączenia. W React Native można to skonfigurować za pomocą onlineManager w połączeniu z wsłuchaczami stanu sieci platformy.

    Na przykład:

    onlineManager.setEventListener(...)
    

    React Query można również skonfigurować do pracy w trybie offline-first, dostosowując w związku z tym jego zachowanie sieciowe.

    Prawdziwy przykład

    Weźmy jako przykład aplikację do czytania wiadomości.

    W ciągu dnia połączenie użytkownika może wyglądać w następujący sposób:

    • Rano: online
    • Po południu: offline
    • Wieczorem: ponownie online

    Przez cały ten okres artykuły wcześniej zapisane w pamięci pozostają dostępne do odczytu, a po przywróceniu połączenia nowe treści mogą zostać automatycznie zsynchronizowane.

    Zastosowania w praktyce

    1. Aplikacje informacyjne

    Co to przynosi:

    • Artykuły w pamięci cache
    • Automaticzne odświeżanie tła
    • Zmniejszone ogólne obciążenie API
    • Możliwość dalszego czytania w trybie offline

    2. Aplikacje e-commerce

    Co to zapewnia:

    • Listy produktów w pamięci cache
    • Zgromadzanie kategorii z wyprzedzeniem
    • Szybsze przemieszczanie się między sekcjami
    • Zauważalnie płynniejsze doświadczenie zakupowe

    React Query umożliwia również wcześniejsze pobieranie danych przed dokonaniem nawigacji, co skraca czas oczekiwania.

    3. Aplikacje z panelami sterowania

    Co to zapewnia:

    • Widgety, które automatycznie się odświeżają
    • Pamięć cache udostępniana na wielu ekranach
    • Zmniejszony ruch sieciowy
    • Znacznie prostsze zarządzanie stanem

    Taki model doskonale pasuje do paneli analitycznych i paneli administracyjnych.

    Co faktycznie usunęliśmy

    Gdy migracja została zakończona:

    Usunięte

    • Samodzielnie opracowana obsługa stanu ładowania
    • Ręczny kod ponawiania prób
    • Duplikowane wywołania API
    • Łącze szablonowe do odświeżania treści
    • Samodzielna logika cache’owania
    • Ręczne zarządzanie ponownym pobieraniem danych

    Dodane

    • Sama biblioteka React Query
    • Klucze zapytań
    • Instancja QueryClient

    Ostateczny efekt: nie było już potrzeby około 500 linii kodu do obsługi API.

    Kiedy React Query może nie być konieczny

    Użycie React Query może być przesadą, jeśli:

    • Plik aplikacji wykonywa tylko kilka wywołań API
    • Dane w tle prawie się nie zmieniają
    • Cache’owanie rzeczywiście nie jest potrzebne
    • Bezpośrednie działanie aplikacji offline nie ma znaczenia w danym przypadku użycia

    Dla większości aplikacji produkcyjnych korzyści z jego użycia zazwyczaj szybko przewyższają początkowy koszt nauki.

    Podsumowanie

    React Query to coś więcej niż tylko kolejny sposób na pobieranie danych.

    Funkcjonuje jako kompletna platforma do zarządzania stanem serwera, eliminując powtarzalny kod API i jednocześnie poprawiając funkcjonowanie buforowania, zachowanie podczas ładowania, efektywność sieciową oraz doświadczenie użytkowników w trybie offline.

    Największą korzyścią nie była sama wydajność.

    Było to zmniejszenie złożoności.

    Mniej kodu. Mniej błędów. Lepsze doświadczenie dla osób korzystających z aplikacji.

    To połączenie sprawiło, że migracja była opłacalna.

    Pozycje pokrewne