Belajar TanStack Query - useQuery Dasar
Episode 4 of 23

Belajar TanStack Query - useQuery Dasar

Episode ini mengajarkan hook pertama dan terpenting: useQuery. Kalian membuat query dengan queryKey dan queryFn, membaca status data, isLoading, isError, error, dan isFetching, serta mempelajari pola query function yang benar dan aman.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Inilah momen yang kalian tunggu: hook pertama yang benar-benar menggantikan pola useEffect plus fetch manual. Dengan useQuery, kalian mendapat data, status loading, error, dan refetch otomatis dalam satu panggilan hook — tanpa menulis satu pun useState untuk menyimpan hasil fetch.

Episode 4 membedah useQuery dari nol: struktur dasar, status-status yang bisa dibaca, dan pola query function yang benar. Ini adalah hook yang akan kalian pakai paling sering selama berkarir dengan TanStack Query.

Query Pertama Kalian

Struktur Dasar useQuery

useQuery menerima satu objek konfigurasi dengan dua anggota wajib: queryKey dan queryFn.

JSQuery pertama
import { useQuery } from "@tanstack/react-query"
 
async function fetchTodos() {
  const res = await fetch("https://jsonplaceholder.typicode.com/todos")
  if (!res.ok) throw new Error("Gagal mengambil todos")
  return res.json()
}
 
function Todos() {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ["todos"],
    queryFn: fetchTodos,
  })
 
  if (isLoading) return <p>Memuat...</p>
  if (isError) return <p>Terjadi error: {error.message}</p>
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

Perhatikan apa yang tidak ada: tidak ada useState, tidak ada useEffect, tidak ada fetch manual di komponen. useQuery mengembalikan sebuah objek status yang langsung bisa dipakai untuk render. queryKey: ["todos"] mengikat data ini ke slot cache todos, dan fetchTodos dipanggil otomatis saat komponen di-mount.

Status yang Dikembalikan

useQuery mengembalikan banyak properti status. Empat yang paling penting untuk pemula:

  • data: hasil queryFn yang sudah sukses.
  • isLoading: true jika query belum punya data sama sekali dan sedang mengambilnya.
  • isError: true jika queryFn melempar error.
  • error: objek error terakhir yang dilempar queryFn.

Ada satu status lagi yang sering tertukar: isFetching, yang true setiap kali fetch berjalan — termasuk refetch di latar belakang. Bedanya: isLoading hanya true saat pertama kali dan belum ada data, sedangkan isFetching true setiap ada aktivitas jaringan.

Pola Query Function

Fungsi Async Murni

queryFn harus berupa fungsi yang mengembalikan Promise. Jangan pernah menulis Promise di dalam queryFn yang sudah di-trigger dari luar, dan jangan menaruh queryFn di dalam JSX karena akan dibuat ulang terus menerus. Cukup definisikan sebagai fungsi bernama seperti contoh di atas, atau sebagai arrow function:

JSqueryFn inline
const { data } = useQuery({
  queryKey: ["todos"],
  queryFn: async () => {
    const res = await fetch("https://jsonplaceholder.typicode.com/todos")
    if (!res.ok) throw new Error("Gagal mengambil todos")
    return res.json()
  },
})

Melewatkan Parameter via Query Key

Untuk query yang butuh parameter — misalnya todo berdasarkan ID — query function menerima sebuah context yang berisi queryKey. Kalian bisa memakai parameter yang sudah ada di dalam queryKey:

JSParameter lewat queryKey
function TodoDetail({ id }) {
  const { data } = useQuery({
    queryKey: ["todos", id],
    queryFn: ({ queryKey }) => {
      const [, todoId] = queryKey
      return fetch(`https://jsonplaceholder.typicode.com/todos/${todoId}`).then(
        (res) => res.json()
      )
    },
  })
 
  return <h1>{data?.title}</h1>
}

Di sini destructuring const [, todoId] = queryKey mengambil parameter dari array key. queryKey: ["todos", id] membuat setiap id memiliki slot cache sendiri — data todo 1 dan todo 2 tidak akan tertukar.

Melempar Error untuk Trigger Retry

TanStack Query hanya tahu sebuah query gagal jika queryFn melempar error. Jika kalian mengembalikan res.ok yang salah sebagai data, library menganggap sukses dan tidak akan mencoba ulang. Karena itu pola if (!res.ok) throw new Error(...) di atas adalah kunci — error inilah yang memicu mekanisme retry bawaan.

Warning

Jika queryFn melempar error, TanStack Query akan menghentikan pengambilan data dan mencoba ulang sesuai konfigurasi retry. Pastikan selalu melempar error, bukan mengembalikan respons yang gagal, agar status isError dan error bekerja dengan benar.

Membandingkan dengan Pola Manual

Mari lihat penghematan yang didapat. Pola manual dari episode 0 butuh useState, useEffect, dan penanganan loading manual. Dengan useQuery, semua itu digantikan oleh satu hook yang juga memberi cache, retry, dan refetch di latar belakang:

Perbandingan mental
manual:  useState + useEffect + fetch + setData + setLoading + setError
useQuery: useQuery( queryKey, queryFn ) → data + isLoading + isError + isFetching

Ringkasan manual: useState + useEffect + fetch menunjukkan berapa banyak state yang diurus manual dulu, dan useQuery mengkonsolidasikan semuanya. Belum lagi fitur cache yang sama sekali tidak ada di pola manual.

Penutup

Episode 4 memberi kalian hook paling fundamental: useQuery. Kalian sekarang bisa membuat query pertama, membaca status data, isLoading, isError, error, dan isFetching, memakai parameter lewat queryKey, serta melempar error dengan benar agar retry bekerja.

Inti yang harus dibawa pulang:

  • useQuery butuh queryKey dan queryFn yang mengembalikan Promise.
  • isLoading menandakan belum ada data; isFetching menandakan ada aktivitas jaringan.
  • Parameter query dilewatkan lewat queryKey dan diambil dari context.
  • Selalu throw error di queryFn agar status error dan retry bekerja.
  • Tidak perlu useState atau useEffect lagi untuk data fetching.
  • Satu queryKey berarti satu slot cache.

Di episode 5 selanjutnya kita akan membahas query keys dan caching — struktur array hierarkis untuk query yang kompleks, peran query key dalam deduplication, dan bagaimana structural sharing menjaga performa. Pemahaman ini menjadi kunci untuk invalidasi cache di episode 6.

Belajar TanStack Query - useQuery Dasar | Belajar TanStack Query