Episode ini membuka inti TanStack Query: QueryClientProvider, hook useQuery, status isPending dan isError, pengaturan staleTime dan gcTime, error handling dengan retry, serta background refetching yang menyinkronkan data secara otomatis.

Setelah setup selesai di episode 3, inilah momen yang kalian tunggu: menulis query pertama. TanStack Query mengubah data fetching dari ritual useEffect yang berulang menjadi satu hook yang melacak status, cache, dan sinkronisasi secara otomatis.
Episode 4 membedah QueryClientProvider, hook useQuery, status yang dikembalikan, pengaturan staleTime dan gcTime, error handling dengan retry, serta background refetching. Semua konsep di episode 2 kini dipraktikkan.
Di akhir episode ini, kalian akan memahami alur kerja query di level komponen dan tahu cara mengatur cache agar aplikasi terasa cepat tanpa kerja ekstra.
queryClient dibuat sekali lalu disuntikkan ke pohon komponen lewat QueryClientProvider. Tanpa provider ini, semua useQuery akan gagal.
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import App from "./App"
const queryClient = new QueryClient()
createRoot(document.getElementById("root")).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</StrictMode>
)<QueryClientProvider client={queryClient}> membungkus seluruh aplikasi. Mulai sekarang, semua hook query di dalam pohon ini memakai cache yang sama.
useQuery menerima objek konfigurasi dengan queryKey dan queryFn, lalu mengembalikan status dan data:
import { useQuery } from "@tanstack/react-query"
function Todos() {
const { data, isPending, isError, error } = useQuery({
queryKey: ["todos"],
queryFn: async () => {
const res = await fetch("/api/todos")
if (!res.ok) throw new Error("Gagal memuat todos")
return res.json()
},
})
if (isPending) return <p>Memuat todos...</p>
if (isError) return <p>Error: {error.message}</p>
return <ul>{data.map((todo) => <li key={todo.id}>{todo.title}</li>)}</ul>
}queryKey: ["todos"] adalah identitas query — unik dan menjadi dasar cache. queryFn mengembalikan promise; saat resolved, hasilnya masuk cache. Status isPending menandakan belum ada data, dan isError menandakan terjadi error.
Dua komponen yang memakai queryKey identik akan berbagi satu query dan satu cache. Ini deduplication: request yang sama tidak dikirim dua kali, dan data langsung tersedia untuk komponen kedua tanpa loading.
staleTime menentukan berapa lama data dianggap segar (belum perlu di-fetch ulang). gcTime menentukan berapa lama data tersimpan di cache setelah tidak dipakai. Keduanya sering tertukar:
const { data } = useQuery({
queryKey: ["profil"],
queryFn: ambilProfil,
staleTime: 60_000,
gcTime: 5 * 60_000,
})Dengan staleTime: 60_000, dalam satu menit data dipakai langsung dari cache tanpa request baru. gcTime: 5 * 60_000 membuat data bertahan lima menit setelah komponen melepasnya.
Secara default, query yang stale di-fetch ulang setiap window kembali fokus dan saat koneksi kembali online. Perilaku ini bisa diatur lewat refetchOnWindowFocus dan refetchOnReconnect. Hasilnya, data selalu tersinkron tanpa tombol refresh manual.
Saat queryFn melempar error, TanStack Query mencoba ulang dengan jeda yang naik secara eksponensial. Kalian bisa mengontrol jumlah percobaan dan penundaan:
const { data, refetch } = useQuery({
queryKey: ["cekout"],
queryFn: ambilCekout,
retry: 3,
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30_000),
})
return (
<button onClick={() => refetch()}>Muat ulang</button>
)retry: 3 membatasi tiga kali percobaan ulang. retryDelay menghitung jeda eksponensial dengan batas maksimum 30 detik. refetch dipanggil manual lewat tombol, memaksa query di-fetch ulang sekarang juga.
TanStack Query menyediakan devtools yang menampilkan semua query, statusnya, data cache, dan tombol untuk menguji refetch serta invalidate:
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider><ReactQueryDevtools /> menambahkan panel di pojok browser. Dari panel ini kalian bisa melihat staleTime efektif, menguji invalidasi, dan memeriksa isi cache — sangat berguna saat debugging di episode 14.
Tip
Buka React Query DevTools lalu klik setiap query untuk melihat isStale dan isFetching. Dua status inilah kunci memahami kapan data di-fetch ulang.
Episode 4 menutup fondasi data fetching: membungkus aplikasi dengan QueryClientProvider, membaca data lewat useQuery dengan status isPending dan isError, mengatur staleTime dan gcTime, menangani error dengan retry, serta memantau semua lewat React Query DevTools.
Inti yang harus dibawa pulang:
Di episode 5 selanjutnya kita akan membahas data mutations dan optimistic updates — useMutation untuk mengubah data, invalidasi query setelah mutasi, optimistic update dengan rollback, serta paginated data dan infinite scrolling dengan useInfiniteQuery. Siapkan aplikasi belajar-tanstack kalian, karena kita mulai menulis mutasi pertama!