Przejdź do treści

Pobieranie danych w React: React Query vs SWR vs RSC

Wybór między TanStack Query, SWR a useEffect w 2026 roku. Sprawdź porównanie narzędzi i zobacz, jak Server Components zmieniają podejście do fetchingu.

Maciej Sala

Founder StriveLab

10 min czytaniaOpublikowano 24 kwietnia 2026 (Aktualizacja 20 maja 2026)

Mamy 2026 rok i na stole wylądowały w zasadzie trzy dominujące rozwiązania: potężny kombajn TanStack Query, zwinny i lekki pakiet SWR od Vercela oraz uparty klasyk useEffect (stosowany głównie przez zespoły cierpiące na alergię na dodawanie nowych bibliotek do pliku package.json). W tym artykule zderzymy ze sobą całą trójkę i sprawdzimy, jak w to wszystko wpisują się rewelacyjne Server Components.

React Query, SWR czy useEffect: szybka ścieżka decyzji

Nie masz czasu na długie lektury? Oto ściągawka w 30 sekund:

  1. Server Components (w Next.js lub Remix). Twoja pierwsza linia frontu. Jeśli coś da się zaciągnąć i wrzucić do HTML-a bezpośrednio z poziomu serwera, zrób to tam. Nie martwisz się o logikę cache'owania, unikasz wpychania JavaScriptu do przeglądarki.
  2. TanStack Query. Podstawowe działo, jeśli jednak potrzebujesz uderzyć po dane z poziomu frontendu (klienta). Potężne, stabilne i przeładowane niezbędnymi funkcjami.
  3. SWR — Gdy budujesz coś bardzo minimalistycznego i potrzebujesz jedynie leciutkiej odskoczni pod podstawowe odpytywania.
  4. Opcja połączenia useEffect z fetch. Zakazane słowo przy twardej architekturze korporacyjnej. Trzymaj to z dala od środowisk produkcyjnych.

Dlaczego useEffect bywa słaby do pobierania danych?

Zacznijmy w ogóle od tego, na jakich minach kładzie nas klasyczne, podręcznikowe podejście:

Code
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
 
  useEffect(() => {
    let cancelled = false
    setLoading(true)
 
    fetch(`/api/users/${userId}`)
      .then((r) => r.json())
      .then((data) => {
        if (!cancelled) {
          setUser(data)
          setLoading(false)
        }
      })
      .catch((err) => {
        if (!cancelled) {
          setError(err)
          setLoading(false)
        }
      })
 
    return () => {
      cancelled = true
    }
  }, [userId])
 
  if (loading) return <Spinner />
  if (error) return <Error error={error} />
  return <Profile user={user} />
}

Powyższy blok kodu technicznie działa, ale problem polega na tym, że skrywa mnóstwo architektonicznych potknięć:

1. Wycieki na zasobach bazy (Brak cache'owania). Wyobraź sobie, że wchodzisz w zakładkę /users/123, potem skaczesz pod /users/456 by za moment znów wrócić na stare /users/123. Przeglądarka zaciągnie kod od zera za każdym jednym razem. Choć rekordy w bazie się nie zmieniły to Ty niepotrzebnie płacisz za to w rachunkach do serwera.

2. Marnowanie łącza (Brak deduplikacji). Jeżeli na jednej stronie pięć różnych małych komponentów zechce odczytać dane z /api/users/123, "goły" fetch potulnie wyśle w świat pięć osobnych requestów. Kompletny absurd i to bardzo powszechny.

3. "Kto pierwszy ten lepszy" (Race conditions). Nawet z opcją zmyślnej blokady (flaga cancelled), jeśli szybko przebierasz między podstronami, asynchroniczne odpowiedzi mogą spłynąć do komponentu w całkowicie złej kolejności. Twój kod wysypie starą odpowiedź, chociaż przed momentem pytałeś już o nową.

4. Panika przy błędach (Brak retry). Padło Ci lokalne połączenie na ułamek sekundy w pociągu? Koniec. Użytkownik dostaje brzydki Error i jeśli sam nie wciśnie klawisza f5 (odśwież), utknie tam na amen.

5. Złe doświadczenia w interfejsie (Brak odświeżeń w tle). Odchodzisz od biurka, w międzyczasie Twoi znajomi dopisali wpisy w grupie. Wracasz za kwadrans, ale strona twardo wisi na tym co widziała przed wyjściem. Bez odświeżania na tło nie wiesz nawet, że informacje są przestarzałe.

6. Frustrujący wskaźnik ładowania (Brak Optymistycznych Aktualizacji). Zostawiasz komentarz, po czym wlepiasz wzrok w kręcące się kółko, modląc się, by proces przebiegł pomyślnie. Nowoczesne systemy pokazują Twój komentarz od razu, nie każąc Ci czekać na zielone światło od .

Widzisz problem? Jako programista mógłbyś samodzielnie zaimplementować te mechanizmy, ale wymagałoby to napisania tysięcy linii kodu i narażenia się na dziesiątki błędów. Nie warto wyważać otwartych drzwi, skoro istnieją gotowe, sprawdzone rozwiązania przygotowane przez doświadczonych specjalistów.

TanStack Query w 2026: kiedy jest najlepszym wyborem?

TanStack Query (w starych dziejach używany pod szyldem React Query) to paczka, która powyższe problemy kompresuje do śmiesznie prostych dziesięciu linijek kodu.

Code
import { useQuery } from '@tanstack/react-query'
 
function UserProfile({ userId }: { userId: string }) {
  const {
    data: user,
    isLoading,
    error,
  } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetch(`/api/users/${userId}`).then((r) => r.json()),
  })
 
  if (isLoading) return <Spinner />
  if (error) return <Error error={error} />
  return <Profile user={user} />
}

Tylko tyle. Co w pakiecie dostarcza Ci ten kod?

  1. Genialnie prosty bufor, który rozbija cache na podstawie podanego klucza (czyli np. ['user', userId]). Każdy kolejny komponent podpięty pod ten klucz, bez pardonu doczepi się do wyniku i daruje sobie szukanie danych na świeżo w bazie.
  2. Magiczna Deduplikacja, jak już wcześniej wspomniałem, wszystkie prośby równoległe zostają zebrane i wysłane jednym zgrabnym "kurierem" (promisem).
  3. Walka do końca (Retry). Domyślnie skrypt walczy jeszcze trzykrotnie, po równych odstępach odrzucenia (exponential backoff) zanim definitywnie "rzuci ręcznikiem".
  4. Natychmiastowe aktualizacje dla wracających. Zmienisz kartę by sprawdzić maila? TanStack na moment przed Twoim powrotem odpyta API, żeby zaserwować Ci na ekran najbardziej "świeżą" treść.
  5. Rewalidacja Stale-while. Pokaże to, co do tej pory uzbierał w buforze w błyskawicznym trybie, żeby tylko w tle móc doładować i uaktualnić wartości.

Setup TanStack Query w aplikacji React

Code
npm install @tanstack/react-query

Teraz mała owijka dla reszty aplikacji:

Code
// src/providers.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
 
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 1000, // Ile czasu dane są u nas uznawane za tzw. świeże (tu ucięte po 1 minucie)
      gcTime: 5 * 60 * 1000, // Jak długo aplikacja przetrzymuje klucze i historię w nieużywanej zakładce - śmieciarz odpala po 5 minutach!
    },
  },
})
 
export function Providers({ children }) {
  return (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  )
}

Mała podpowiedź jeśli pracujesz nad stosem od Next.js z App Routerem to upewnij się, żeby Twój wbudowany "klient do zapytań" stał się funkcją opartą per każde wejście a nie o jeden instancyjny, wspólny "singleton". Skutecznie przeprowadzi Cię przez to dokumentacja na oficjalnych stronach.

Domyślne ustawienia TanStack Query, które trzeba znać

TanStack narzuca swoje żelazne i niezwykle czułe ramy na cały framework. Warto znać je sobie przybliżyć:

Ustawienie StartoweZa co opowiada u podstaw?Kiedy powinieneś interweniować i je przesterować?
staleTime: 0W tej paczce wszystko, dosłownie "natychmiast" bywa traktowane za przeterminowane (stale). Odświeżenia i pule pobrań w tle odpalają w sekundę po przeładowaniach widoków!Odmierz spokojnie np. 60 do 120 sekund czasu dla mało krytycznych miejsc pod odświeżanie serwerowe by portfele u inwestora nie poszły z ogniem.
gcTime: 5 * 60 * 1000Gdyby skrypt o tobie kompletnie zapomniał (bo przeszedłeś w panel na całkowicie inny model komponentowy) to po pełnych pięciu minutach zapas bufora poleci "w kosz".Wyjdź w wyższe ramy, kiedy użytkownicy Twojego klienta notorycznie lubią wracać do wielkich rozstrzelanych i wczytanych wcześniej wielkich raportów.
retry: 3Każde polegnięcie API z reguły stawi czoła walce ponownej (z tzw. wariantem podwójnego odstępu w próbach).Kompletnie wyklucz z tego wyścigu m.in. ścieżki w procesach opartych o błędy autoryzacji. W końcu błędne hasło to błędne hasło.
refetchOnWindowFocus: truePowrót w zakładce czy nawet kliknięcie po obiekcie do ekranu wyrzuci ponowne przeładowanie zapytań serwera.Możesz to temperować po wspomnianej wcześniej zaporze czasowej u "StaleTime".

Mutacje w TanStack Query i zmiana danych

Code
import { useMutation, useQueryClient } from '@tanstack/react-query'
 
function AddComment({ postId }: { postId: string }) {
  const queryClient = useQueryClient()
 
  const mutation = useMutation({
    mutationFn: (text: string) =>
      fetch(`/api/posts/${postId}/comments`, {
        method: 'POST',
        body: JSON.stringify({ text }),
      }),
    onSuccess: () => {
      // Skasowaliśmy bufor dla tego posta by zmusić paczkę do uaktualnień!
      queryClient.invalidateQueries({ queryKey: ['comments', postId] })
    },
  })
 
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        const text = new FormData(e.currentTarget).get('text') as string
        mutation.mutate(text)
      }}
    >
      <input name="text" />
      <button disabled={mutation.isPending}>Wyślij</button>
    </form>
  )
}

Wystarczy jeden wpis, aby odświeżyć dane bez zbędnej zwłoki. Gdy operacja zakończy się sukcesem, bufor zapytań dla komentarzy powiązanych z danym postId jest natychmiast unieważniany. W rezultacie TanStack Query automatycznie pobiera najświeższe dane z bazy i aktualizuje widok dyskusji dla wszystkich użytkowników.

Optimistic updates w TanStack Query

Nikt nie lubi czekać na potwierdzenie operacji, zwłaszcza gdy dane muszą pokonać długą drogę do serwerów zlokalizowanych w innym regionie świata. Zamiast niepotrzebnie zmuszać użytkownika do obserwowania stanu oczekiwania, zastosuj interfejs w stylu Messengera, czyli natychmiast odśwież widok aplikacji, dając użytkownikowi złudzenie sukcesu, podczas gdy faktyczna synchronizacja z bazą danych odbywa się w tle:

Code
const mutation = useMutation({
  mutationFn: (text: string) => addComment(postId, text),
  onMutate: async (text) => {
    // Wciskasz hamulec by w tym wariancie nic Ci się po bazie przez ułamki na ekranach przypadkiem nie ułożyło z opóźnień:
    await queryClient.cancelQueries({ queryKey: ['comments', postId] })
 
    // Zrób fotkę ze stanu do tej pory wypracowanych rozmów (na wypadek katastrofy sieci i potrzeby cofnięć awaryjnych)
    const previousComments = queryClient.getQueryData(['comments', postId])
 
    // Optymistycznie dodajemy komentarz do cache, zanim serwer potwierdzi zapis
    queryClient.setQueryData(['comments', postId], (old: Comment[]) => [
      ...old,
      { id: 'temp-' + Date.now(), text, createdAt: new Date() },
    ])
 
    return { previousComments }
  },
  onError: (err, text, context) => {
    // Jeżeli faktycznie jednak poszło coś do sieci źle, wgrywasz bezpiecznie zapasowy punkt dawnej dyskusji bez żadnego potu
    queryClient.setQueryData(['comments', postId], context.previousComments)
  },
  onSettled: () => {
    // Potem, by ukrócić wszystkie wariacje "szkieletu kodu u fałszywek bufora", żądasz rzutowania w stan "odśwież natychmiast całą logikę komentarza by odetchnąć i zamknąć puste identyfikatory logiki "temp-Date.now()""
    queryClient.invalidateQueries({ queryKey: ['comments', postId] })
  },
})

Cały kod zajmuje trochę więcej niż nowe bajery po paczce w postaci haków w najnowszych, bazowych systemach (np. useOptimistic od React z lat 19) ale uwierz mi — otrzymujesz na nim wielokrotnie precyzyjniejsze kontrolki kierownicze.

Infinite queries dla długich list

Paginacje na "Load More" dla systemów bazodanowych?

Code
import { useInfiniteQuery } from '@tanstack/react-query'
 
function PostList() {
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
    useInfiniteQuery({
      queryKey: ['posts'],
      queryFn: ({ pageParam = 1 }) =>
        fetch(`/api/posts?page=${pageParam}`).then((r) => r.json()),
      getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,
      initialPageParam: 1,
    })
 
  return (
    <>
      {data?.pages.map((page, i) => (
        <Fragment key={i}>
          {page.posts.map((post) => (
            <PostCard key={post.id} post={post} />
          ))}
        </Fragment>
      ))}
 
      {hasNextPage && (
        <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
          {isFetchingNextPage ? 'Ładowanie w toku...' : 'Dawaj więcej wpisów!'}
        </button>
      )}
    </>
  )
}

Jeśli przy okazji renderujesz bardzo długie listy wyników, połącz to z wirtualizacją, by nie obciążać przeglądarki tysiącami elementów DOM — szczegóły w artykule Wirtualizacja list w React — kiedy TanStack Virtual ratuje FPS.

SWR w React: lekki fetching i odświeżanie danych

SWR to zwinna alternatywa dla TanStack Query, rozwijana przez zespół Vercel. Oferuje zbliżone możliwości, ale jest znacznie lżejsza. Podczas gdy TanStack Query zajmuje około 13 KB, SWR waży zaledwie 4 KB, co pozwala znacząco ograniczyć rozmiar paczki w Twojej aplikacji.

Code
import useSWR from 'swr'
 
function UserProfile({ userId }: { userId: string }) {
  const {
    data: user,
    isLoading,
    error,
  } = useSWR(`/api/users/${userId}`, (url) => fetch(url).then((r) => r.json()))
 
  if (isLoading) return <Spinner />
  if (error) return <Error error={error} />
  return <Profile user={user} />
}

Przy bliższym spojrzeniu oba narzędzia mają dużo wspólnego, ale różnią się w kilku istotnych punktach:

Prostota. Tu wygrywa SWR. Ma mniejsze API i niższy próg wejścia, więc do prostych projektów i mniejszych aplikacji jest wygodniejszym wyborem.

Bogactwo funkcji. Tu wygrywa TanStack Query. Daje znacznie więcej narzędzi do złożonych aplikacji: dynamiczne klucze, rozbudowaną inwalidację cache po mutacjach, optymistyczne aktualizacje i mechanizm anulowania zapytań (query cancellation), który natychmiast przerywa fetch, gdy użytkownik np. cofnie się przed załadowaniem danych.

Typowanie TypeScript. TanStack Query ma dopracowaną integrację z TypeScriptem, z dobrą inferencją typów bez nadmiaru ręcznych adnotacji generycznych.

Podsumowując, do rozbudowanych aplikacji z wieloma widokami, panelami i mutacjami (dashboardy, SaaS, e-commerce) bierz TanStack Query, a do małych, prostszych projektów (małych paneli, stron marketingowych) wystarczy lżejszy SWR. Oba są solidne i sprawdzone w produkcji.

TanStack Query, SWR i React Server Components

Złota reguła architektury w App Routerze brzmi: zanim pobierzesz dane na kliencie, sprawdź, czy w ogóle muszą tam trafić. W nowych aplikacjach Next.js większość wstępnego pobierania danych powinna odbywać się na serwerze, wewnątrz Server Components:

Code
// app/users/[id]/page.tsx — Server Component (Next.js 15+)
async function UserPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  const user = await fetchUser(id) // pobranie wykonuje się na serwerze
  return <Profile user={user} />
}

Do przeglądarki nie trafia wtedy żaden JavaScript fetchujący, serwer pobiera dane i wysyła gotowy HTML. Znikają wodospady zapytań po stronie klienta i koszt wykonania na słabszych urządzeniach. Ten sam model „zero JS domyślnie" opisuję szerzej przy okazji architektury wysp w Astro.

Czego Server Components same nie załatwią? Wszystkiego, co dzieje się interaktywnie po stronie klienta:

  • Dynamiczne filtry, paginacja i auto-uzupełnianie, gdy użytkownik klika i odświeża fragment widoku bez przeładowania strony.
  • Dane real-time. Powiadomienia, czat, statystyki na żywo przez albo .
  • Mutacje danych. Formularze zapisujące zmiany na serwerze, które potem aktualizują widok.

W tych przypadkach wraca TanStack Query (albo SWR) po stronie klienta.

Prehydracja jako wzorzec łączenia klienta i serwera

Najmocniejsze podejście łączy oba modele, czyli Server Component pobiera dane początkowe, a kliencki TanStack Query przejmuje je jako stan startowy i dalej zarządza odświeżaniem. Użytkownik dostaje gotowy widok natychmiast (dobre dla i pierwszego renderu), a interaktywność i cache działają jak w czystej aplikacji klienckiej:

Code
// app/users/[id]/page.tsx — Rzutowanie od Next.js z 15+ u rzutni z mocą od zapleczy Server Component
import { UserClient } from './user-client'
 
async function UserPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  const initialUser = await fetchUser(id)
  return <UserClient userId={id} initialData={initialUser} />
}
Code
// app/users/[id]/user-client.tsx — Moduł rzucony u przeglądania u klienta "Client Component"
'use client'
 
import { useQuery } from '@tanstack/react-query'
 
export function UserClient({ userId, initialData }) {
  const { data: user } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    initialData,
    staleTime: 60 * 1000,
  })
 
  return <Profile user={user} />
}

Dzięki temu zyskujesz zalety obu podejść, z jednej strony, serwer renderuje gotowy HTML pod SEO i szybki pierwszy render, a z drugiej, klient dostaje inteligentny cache TanStack Query do odświeżania danych na żywo.

Kiedy klasyczny useEffect nadal wystarczy?

Ręczny fetch w useEffect ma dziś już bardzo wąskie zastosowanie i są w zasadzie trzy sytuacje, w których nadal bywa właściwy:

  1. Strumienie danych. WebSockets albo SSE, gdzie potrzebujesz utrzymać połączenie i subskrypcję, a nie wykonać pojedyncze pobranie. TanStack Query jest projektowany pod request-response, nie pod ciągły strumień.
  2. Jednorazowe efekty po załadowaniu, czyli proste akcje, które nie wymagają cache, retry ani inwalidacji.
  3. Integracje z zewnętrznymi SDK, czyli np. nasłuch zmian autoryzacji albo realtime z Firebase, gdzie podpinasz się pod cykl życia biblioteki.

Poza tymi przypadkami ręczny fetch w useEffect do pobierania danych aplikacji to antywzorzec — generuje wodospady, brak cache, brak retry i problemy z race conditions. Szczegółowo rozbieram to w artykule useEffect to prawie zawsze błąd — kiedy go naprawdę potrzebujesz, a kiedy nie.

Najczęstsze błędy z TanStack Query

W przeglądach kodu klientów te trzy pomyłki wracają najczęściej:

1. Niestabilny klucz zapytania. Jeśli do queryKey wstawisz wartość zmieniającą się przy każdym renderze (np. Date.now()), cache nigdy nie trafi. Każde zapytanie ma inny klucz, więc TanStack pobiera dane od nowa za każdym razem.

Code
// ŹLE — klucz zmienia się przy każdym renderze, cache bezużyteczny
useQuery({ queryKey: ['posts', Date.now()], queryFn: ... });
 
// DOBRZE — klucz oparty na realnych parametrach zapytania
useQuery({ queryKey: ['posts', { category, page }], queryFn: ... });

2. Tworzenie QueryClient wewnątrz komponentu. Jeśli instancja QueryClient powstaje przy każdym renderze (zamiast raz, w stabilnym miejscu), cały cache resetuje się na każdej zmianie drzewa. QueryClient twórz raz w useState/module, a potem przekazuj do <QueryClientProvider>.

3. Trzymanie danych serwerowych w Zustand albo Reduxie. Ręczne kopiowanie danych z serwera do globalnego store to praca, którą TanStack Query wykonuje automatycznie — z cache, inwalidacją i odświeżaniem. Globalny store zostaw na stan czysto kliencki (, preferencje), a dane serwerowe powierz TanStack Query. Kiedy co wybrać do zarządzania stanem, rozbieram w artykule o Context, Zustand i URL state.

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.
Next.js

Często zadawane pytania

Czego użyć do pobierania danych w React w 2026 — TanStack Query, SWR czy useEffect?

Oto prosta ścieżka decyzyjna: (1) Masz do dyspozycji Server Components? Świetnie, pobieraj dane bezpośrednio tam, a przeglądarka dostanie gotowy HTML bez żadnego fetcha po stronie klienta. (2) Potrzebujesz potężnych interakcji, mutacji i zaawansowanego cache'owania w przeglądarce? Zdecydowanie bierz TanStack Query (dawniej znane jako React Query). (3) Robisz coś prostego, gdzie wystarczy lekki cache na poziomie odczytu? SWR będzie w sam raz. (4) Co z useEffect + fetch? Traktuj to raczej jako antywzorzec. Przydaje się tylko w absolutnie niszowych przypadkach, np. gdy na szybko budujesz poboczne narzędzie dla deweloperów, bez produkcyjnego ciśnienia.

Gdzie leży główna różnica między TanStack Query a SWR?

TanStack Query (ważące około 13 KB po spakowaniu) to prawdziwy kombajn: daje Ci natychmiastowe (optymistyczne) aktualizacje interfejsu, nieskończone zapytania, zautomatyzowane invalidacje, rewelacyjne narzędzia deweloperskie i bardzo silne typowanie w TS. SWR (leciutkie, ledwie ~4 KB) jest z kolei niezwykle minimalistyczne, opiera się na strategii "stale-while-revalidate" i bryluje przy bardzo szybkim, bezstresowym odczycie. Dla dużych produktów SaaS wchodź w TanStack. Do małych dashboardów i blogów SWR zrobi robotę.

Czy to prawda, że TanStack Query wygryzie z projektów Reduxa lub Zustand?

Nie, to częste nieporozumienie. TanStack Query dba o stan serwerowy (czyli trzyma w ryzach użytkowników, posty, produkty pobrane z API). Redux czy Zustand to zarządcy stanu po stronie klienta (trzymają koszyki zakupowe, rozwinięte menu czy filtry wyszukiwania). To dwie różne dziedziny. W 2026 roku to bardzo dobry zestaw: TanStack Query (do API) + Zustand (do UI) + parametry w URL (do filtrów i paginacji). Redux nadal znajduje swoje miejsce głównie w ogromnych projektach typu enterprise.

Czy TanStack Query dobrze współpracuje z nowymi Next.js Server Components?

Jak najbardziej. Ekipa od TanStack wypuściła specjalne API ułatwiające prehydrację: najpierw "ściągasz" dane na poziomie serwera używając queryClient.prefetchQuery(), następnie je "suszysz" (dehydrate) i przerzucasz przez komponent <HydrationBoundary> prosto do Client Componentu. Efekt? Przeglądarka chwyta stan z cache'u zamiast uderzać drugim, niepotrzebnym wywołaniem fetcha do bazy. Użytkownik nie widzi "loadingów", a odświeżanie w tle zachowuje się tak jak należy.

W jakich momentach TanStack Query samo, w tle, odświeża nam dane?

Z automatu robi to w czterech kluczowych momentach: (1) gdy montujesz nowy komponent, a dane zdążyły się już przeterminować (zgodnie z staleTime), (2) gdy po prostu wracasz do zakładki w przeglądarce (refetchOnWindowFocus), (3) gdy na chwilę urwie Ci internet i wróci połączenie (refetchOnReconnect), (4) gdy ustawisz sztywny zegarek odpytywania przez refetchInterval. Oczywiście zawsze masz pod ręką ręczne, brutalne odświeżenie wywołując queryClient.invalidateQueries().

Czy te wszystkie możliwości nie zabiją mi wydajności wagi aplikacji (bundle size)?

Rdzeń @tanstack/react-query waży około 13 KB (gzipped). Choć biblioteka DevTools dodaje kolejne 30 KB, ładuje się ona wyłącznie w środowisku deweloperskim i nie obciąża paczki produkcyjnej. SWR jest lżejszy (ok. 4 KB), jednak przy dzisiejszej wadze samego Reacta przekraczającej 100 KB, różnice te mają znaczenie marginalne.

Ale ja naprawdę lubię używać klasycznego useEffect i fetch. Można tak nadal pisać kod?

Można, nikt Ci tego nie zabroni, ale w 95% przypadków tworzysz sobie na własne życzenie bombę z opóźnionym zapłonem (lub antywzorzec). "Goły" useEffect nie wie nic o współdzieleniu cache'u między komponentami, nie ogarnie zjawiska nadpisywania sobie danych (race conditions), nie wie co to powtórzenie żądania przy zerwaniu łącza i nie odświeży Ci interfejsu automatycznie. Jeśli zaczniesz to wszystko ręcznie kleić od zera, to ostatecznie napiszesz bardzo dziurawą i słabą kopię TanStack Query. Szkoda życia, więc użyj sprawdzonej biblioteki.

O autorze

Maciej Sala

Maciej Sala — konsultant technologiczny produktów cyfrowych i web developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwija własne projekty.

Pomagam przekładać takie tematy na konkretne wdrożenia w frontendzie, SEO, analityce i procesie produktowym.

Skontaktuj się ze mną

Biblioteka wiedzy na temat Next.js

Czytaj dalej

Zobacz więcej wpisów
Zaawansowane tabele danych z TanStack Table w Next.js

Tabele HTML z są proste, ale dodanie sortowania, filtrowania, paginacji, selekcji wierszy i responsywności wymaga więcej pracy i setek linii kodu. TanStack Table dawniej React Table to headless biblioteka, która dostarcza logikę tabeli, a po naszej stronie zostawia pełną kontrolę nad renderowaniem.

Maciej Sala

Maciej Sala

Founder StriveLab

React Server Components i Server Actions: SEO i wydajność

App Router pozwala przesunąć pobieranie danych i renderowanie treści na serwer, zostawiając w przeglądarce małe obszary interakcji. Taki podział może ograniczyć JavaScript i ułatwić dostarczenie treści w HTML, ale nie gwarantuje dobrych Core Web Vitals ani indeksacji. Wynik zależy też od czasu odpowiedzi serwera, cache, obrazów, fontów, metadanych i bezpieczeństwa mutacji.

Maciej Sala

Maciej Sala

Founder StriveLab

Obsługa formularzy przez React 19 Actions i useActionState

React 19 pozwala przekazać funkcję bezpośrednio do atrybutu action elementu . Dzięki integracji z Actions, useActionState , useFormStatus i useOptimistic możesz ograniczyć ręczną obsługę onSubmit , pending state oraz optymistycznych aktualizacji , zachowując kontrolę nad błędami i stanem trwałym.

Maciej Sala

Maciej Sala

Founder StriveLab