Episode ini membahas query paralel dengan useQueries dan komposisi query, serta dependent query yang menunggu hasil query lain lewat opsi enabled. Kalian belajar pola pengambilan data yang saling bergantung secara benar.

Aplikasi nyata jarang mengambil satu data saja. Sebuah dashboard butuh statistik, notifikasi, dan daftar aktivitas sekaligus. Halaman profil butuh data user dan post miliknya — dan post baru bisa diambil setelah user diketahui. Episode 8 mengajarkan dua pola untuk menangani kompleksitas ini: query paralel dan dependent query.
Query paralel artinya beberapa query berjalan bersamaan. Cara paling sederhana adalah memanggil beberapa useQuery sekaligus di satu komponen:
function Dashboard() {
const stats = useQuery({ queryKey: ["stats"], queryFn: fetchStats })
const notifs = useQuery({ queryKey: ["notifs"], queryFn: fetchNotifs })
const activity = useQuery({ queryKey: ["activity"], queryFn: fetchActivity })
return <div>...</div>
}Ketiga query di atas berjalan paralel karena tidak ada ketergantungan di antara mereka. Setiap useQuery punya cache dan status sendiri — cara ini simpel dan cukup untuk sebagian besar kasus.
Ada kalanya jumlah query tidak diketahui saat menulis kode — misalnya query untuk setiap item dalam sebuah list. Di sinilah useQueries unggul:
import { useQueries } from "@tanstack/react-query"
function TodoBatch({ ids }) {
const results = useQueries({
queries: ids.map((id) => ({
queryKey: ["todos", id],
queryFn: () => fetchTodo(id),
})),
})
const semuaSelesai = results.every((r) => r.isSuccess)
if (!semuaSelesai) return <p>Memuat...</p>
return <ul>{results.map((r) => <li key={r.data.id}>{r.data.title}</li>)}</ul>
}useQueries menerima objek dengan array queries, dan mengembalikan array hasil yang sejajar dengan input. ids.map menghasilkan konfigurasi query untuk setiap id, dan semua query berjalan paralel. Hasil results[i] adalah objek status yang sama seperti dari useQuery.
Dependent query adalah query yang baru berjalan setelah query lain selesai, karena datanya dibutuhkan sebagai parameter. Kunci pengendalinya adalah opsi enabled:
function UserPosts({ userId }) {
const user = useQuery({
queryKey: ["user", userId],
queryFn: () => fetchUser(userId),
enabled: !!userId,
})
const posts = useQuery({
queryKey: ["user", userId, "posts"],
queryFn: () => fetchUserPosts(userId),
enabled: !!user.data,
})
if (user.isLoading) return <p>Memuat user...</p>
if (posts.isLoading) return <p>Memuat post...</p>
return <div>{posts.data.map((p) => <p key={p.id}>{p.title}</p>)}</div>
}Query posts di-set enabled: !!user.data, jadi dia menunggu user.data tersedia sebelum berjalan. enabled: !!userId pada query pertama memastikan query tidak dijalankan sebelum userId valid — pola yang mencegah fetch sia-sia.
Tanpa enabled, query tetap berjalan meskipun parameternya belum tersedia. Akibatnya queryFn menerima undefined dan melakukan request yang salah atau melempar error. enabled memberi kalian kendali eksplisit kapan sebuah query boleh memulai:
// salah: posts langsung jalan walau user belum ada
const posts = useQuery({
queryKey: ["posts", userId],
queryFn: fetchPosts,
})
// benar: posts menunggu user.data
const posts = useQuery({
queryKey: ["user", userId, "posts"],
queryFn: () => fetchUserPosts(userId),
enabled: !!user.data,
})Perbedaan enabled: !!user.data membuat query kedua menunggu data user. useQuery dengan enabled: false masuk status pending dan tidak menambah beban jaringan sampai enabled berubah menjadi true.
Pola enabled: !!userId juga berfungsi sebagai guard: jika userId kosong, query tidak berjalan sama sekali. Ini berguna untuk halaman yang menunggu input — misalnya dropdown yang memilih ID sebelum detail dimuat.
Warning
Jangan menaruh fetch di luar queryFn hanya karena ingin menunggu data. Selalu gunakan enabled dan biarkan TanStack Query yang mengatur kapan queryFn dipanggil — ini menjaga status query, cache, dan retry tetap konsisten.
Untuk rantai dependensi yang lebih panjang — user → profil → pengaturan — gabungkan enabled secara bertingkat. Setiap tingkat menunggu data tingkat sebelumnya:
user (enabled: !!userId)
→ profile (enabled: !!user.data)
→ settings (enabled: !!profile.data)Diagram user → profile → settings menggambarkan alur sekuensial yang benar. enabled bisa menerima nilai boolean dari status data apa pun, sehingga rantai sepanjang apa pun tetap deterministik.
Episode 8 memberi dua senjata untuk data yang banyak dan saling bergantung: query paralel dengan useQueries untuk daftar dinamis, dan dependent query dengan enabled untuk pengambilan sekuensial yang benar.
Inti yang harus dibawa pulang:
useQuery atau useQueries.useQueries cocok untuk daftar query dinamis yang jumlahnya tidak tetap.enabled menghentikan query sampai kondisinya terpenuhi.enabled: !!data.enabled juga berfungsi sebagai guard untuk parameter kosong.enabled bertingkat.Di episode 9 selanjutnya kita akan membahas pagination dan infinite queries — useInfiniteQuery dengan getNextPageParam untuk tombol Load More, serta pagination sederhana dengan placeholderData dan keepPreviousData untuk UX yang halus tanpa flicker.