Pobieranie danych w React i Next.js porównane na jednej aplikacji
Pobieranie danych w React: serwer, TanStack Query, SWR | We.Inc
Wybierz sposób pobierania danych w React w krótkim quizie, a potem porównaj server components, TanStack Query i SWR pod kątem cache, mutacji i ustawień domyślnych na jednej przykładowej aplikacji.
Pobieraj dane na serwerze, kiedy tylko możesz: w App Router Next.js asynchroniczny server component czeka na dane, zanim zostanie wysłany jakikolwiek HTML, bez kodu spinnera ładowania. Dla danych, które zmieniają się, gdy strona jest otwarta, użyj cache po stronie klienta, takiego jak TanStack Query lub SWR, który obsługuje cache, ponawianie i mutacje. Zwykły fetch w useEffect nadaje się do jednorazowych żądań.
Które podejście pasuje
Podejście
Gdzie działa
Cache
Najlepsze do
Asynchroniczny server component
Serwer
Domyślnie bez cache, włączasz przez use cache
Dane strony, sekrety, zapytania do bazy danych
TanStack Query
Przeglądarka
Cache zapytań według queryKey
Dashboardy, mutacje, stronicowanie
SWR
Przeglądarka
Cache według klucza żądania
Widoków głównie do odczytu, które mają być aktualne
useEffect + fetch
Przeglądarka
Brak, chyba że go napiszesz
Jednego małego żądania w małej aplikacji
Każdy przykład na tej stronie ładuje i edytuje tę samą małą listę notatek, więc różnice wynikają z podejścia, a nie z dema. Odpowiedz na trzy pytania poniżej, aby dostać rekomendację na start i pasujący do niej fragment kodu.
Powiedz, czy używasz frameworka z server components, takiego jak App Router w Next.js.
Powiedz, czy dane muszą być aktualne, gdy strona jest otwarta, i czy użytkownicy je edytują.
Przeczytaj rekomendację i skopiuj pasujący fragment kodu.
Przed wdrożeniem sprawdź ustawienia domyślne w sekcjach porównawczych poniżej.
Uruchamiaj żądania razem. Dwa await jeden po drugim tworzą kaskadę: najpierw utwórz obie obietnice, a potem await Promise.all.
Ustaw ten sam queryKey w każdym useQuery, który czyta te same dane, aby komponenty współdzieliły jedno żądanie i jeden wpis w cache.
Po mutacji unieważnij odpowiedni klucz zamiast ręcznie łatać cache, chyba że potrzebujesz natychmiastowej optymistycznej aktualizacji.
Nigdy nie pobieraj sekretów w client component. Wszystko, o co prosi przeglądarka, ludzie mogą zobaczyć w panelu sieci.
Najczęściej zadawane pytania
Czy żądania fetch są cache'owane w App Router Next.js?
Według aktualnej dokumentacji Next.js żądania fetch domyślnie nie są cache'owane i blokują renderowanie, dopóki się nie zakończą. Identyczne żądania w jednym renderze są memoizowane, a cache włączasz dyrektywą use cache albo strumieniujesz świeże dane wewnątrz Suspense.
Jakie są ważne ustawienia domyślne TanStack Query?
Dane stają się nieaktualne od razu (staleTime 0), więc są pobierane ponownie przy montowaniu, powrocie fokusu do okna i ponownym połączeniu. Nieużywane zapytania zostają w pamięci przez 5 minut (gcTime), a nieudane są ponawiane 3 razy z wykładniczym opóźnieniem. Zwiększ staleTime, jeśli takie ponowne pobieranie to więcej, niż potrzebujesz.
TanStack Query czy SWR?
Oba cache'ują według klucza i odświeżają dane po powrocie fokusu i ponownym połączeniu. TanStack Query ma więcej wbudowanych funkcji do mutacji, unieważniania i devtools, a SWR ma mniejsze API zbudowane wokół useSWR(key, fetcher). Wybierz jedną bibliotekę na aplikację, bo używanie obu duplikuje cache.
Czy przy server components nadal potrzebuję biblioteki klienckiej?
Do ładowania stron często nie. Potrzebujesz jej, gdy dane muszą się odświeżać bez nawigacji, gdy użytkownicy edytują i oczekują natychmiastowej reakcji, albo przy nieskończonych listach. Wiele aplikacji ładuje dane na serwerze, a cache po stronie klienta stosuje tylko na interaktywnych ekranach.
Jak na serwerze sprawdzić, kto jest zalogowany przez Supabase?
Poradnik Supabase o renderowaniu po stronie serwera mówi, by nigdy nie ufać getSession() w kodzie serwerowym. Wywołaj supabase.auth.getClaims(), które weryfikuje podpis tokenu, a potem odpytuj tabele: row level security nadal obowiązuje przy każdym zapytaniu.
Najpierw serwer, potem cache po stronie klienta tam, gdzie trzeba
Server component może bezpośrednio odpytać bazę danych lub wywołać API z danymi uwierzytelniającymi, a wynik dociera jako HTML. To usuwa kod stanów ładowania z większości stron i trzyma klucze poza paczką przeglądarki.
Ekrany interaktywne to inna sprawa. Lista notatek, do której ludzie dodają wpisy, którą filtrują i do której wracają, zyskuje na cache po stronie klienta, który pamięta wyniki między ekranami i odświeża je w tle.
Strumieniowanie zamiast czekania
Jeśli jedno żądanie jest wolne, owiń komponent, który go potrzebuje, w Suspense z fallbackiem. Reszta strony zostaje wysłana od razu, a wolna część dopływa strumieniem. Plik loading.js robi to samo dla całego segmentu trasy w Next.js.
W client component możesz też odebrać obietnicę z serwera i odczytać ją za pomocą API use z React wewnątrz Suspense.
Dlaczego zwykły wzorzec z useEffect nie wystarcza
Fetch w useEffect nie ma cache, więc każde montowanie powtarza żądanie, a wyścigi, ładowanie i błędy trzeba obsłużyć ręcznie. Do jednego małego widżetu to w porządku. Przy czymś większym biblioteka daje Ci to wszystko w kilku liniach, a podstawy samego fetch omawiamy w naszym poradniku o async i fetch.
Ustawienia domyślne, które zaskakują
Zachowanie bez konfiguracji
Zachowanie
Fetch na serwerze w Next.js
TanStack Query
SWR
Cache między żądaniami
Nie, włączasz przez use cache
W pamięci, na kartę przeglądarki
W pamięci, na kartę przeglądarki
Zduplikowane żądania
Memoizowane w obrębie jednego renderu
Współdzielone według queryKey
Deduplikowane według klucza w ciągu 2 sekund
Ponowne pobranie po powrocie fokusu
Nie dotyczy
Tak, gdy dane są nieaktualne
Tak
Ponowne pobranie po ponownym połączeniu
Nie dotyczy
Tak, gdy dane są nieaktualne
Tak
Ponawianie po błędzie
Obsłuż to sam
3, z wykładniczym opóźnieniem
Tak (errorRetryInterval 5000 ms)
Jak długo trzymane są nieużywane dane
Nie dotyczy
5 minut (gcTime)
Brak udokumentowanej wartości domyślnej
Unikanie kaskad na serwerze
Uruchom oba żądania, a potem czekaj na nie razem
Wywołanie obu funkcji przed await sprawia, że żadna nie czeka na drugą.
Strumieniuj wolną część przez Suspense
Nagłówek jest wysyłany od razu, a lista notatek dopływa, gdy przyjdą jej dane.
Supabase w server component
Ten przykład używa helpera createClient z poradnika Supabase o Next.js po stronie serwera (lib/supabase/server.ts, zbudowany na @supabase/ssr). getClaims() weryfikuje token zalogowanego użytkownika, zanim zapytanie zostanie wykonane.
Notatki zalogowanego użytkownika ładowane na serwerze
Row level security na tabeli notes decyduje, które wiersze zostaną zwrócone.
Wybierz podejście
Czy używasz server components (na przykład App Router w Next.js)?
Czy dane muszą się odświeżać, gdy strona pozostaje otwarta?
Czy użytkownicy edytują te dane na ekranie?
Tak
Nie
Fragment kodu na start
Kopiuj fragment
Załaduj dane w asynchronicznym server component. Cache po stronie klienta dodaj później tylko dla części, które staną się interaktywne.
Użyj TanStack Query: useQuery do odczytu, useMutation do zapisu i invalidateQueries do odświeżenia po zmianie.
Użyj SWR: useSWR utrzymuje widok aktualny po powrocie fokusu i ponownym połączeniu przy bardzo małej ilości kodu.
Tutaj wystarczy pojedynczy fetch w useEffect. Posprzątaj za pomocą flagi ignore, aby spóźniona odpowiedź nie nadpisała nowszego stanu.