Belajar TanStack Query - Integrasi dengan State Management
Episode 16 of 23

Belajar TanStack Query - Integrasi dengan State Management

Episode ini membahas pola hybrid: TanStack Query untuk server state dan Zustand atau Redux untuk UI state, serta memperlakukan cache sebagai single source of truth dengan getQueryData agar data tidak diduplikasi di dua tempat.

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

Pendahuluan

Kesalahan arsitektur yang paling umum di aplikasi React: menyimpan data server di store global. Developer menaruh hasil fetch ke Redux atau Zustand, lalu dua sistem saling bersaing mengelola data yang sama — TanStack Query sudah menyimpan cache, store juga menyimpan salinannya, dan keduanya bisa bertengkar.

Episode 16 memperkenalkan pola hybrid: TanStack Query mengelola server state, Zustand atau Redux mengelola UI state. Lalu kita membahas cara memperlakukan cache sebagai single source of truth.

Pola Hybrid

Siapa Mengelola Apa

Pembagian tugasnya tegas. TanStack Query menangani semua data yang berasal dari server — fetch, cache, retry, invalidasi. Store menangani state yang lahir dan mati di client:

Pembagian tanggung jawab
TanStack Query  → server state: todos, users, profil, notifikasi
Zustand/Redux   → UI state: modal terbuka, theme, keranjang, filter aktif

Pembagian TanStack Query server state dan Zustand/Redux UI state membuat setiap sistem mengelola domain yang jelas. Tidak ada dua sumber kebenaran untuk data yang sama.

Contoh dengan Zustand

Zustand cocok untuk UI state yang ringan. Contoh keranjang yang menyimpan id produk (UI state) sementara data produk diambil dari TanStack Query:

JSStore Zustand untuk UI state
import { create } from "zustand"
 
export const useCartStore = create((set) => ({
  productIds: [],
  cartOpen: false,
  addProduct: (id) =>
    set((state) => ({ productIds: [...state.productIds, id] })),
  toggleCart: () => set((state) => ({ cartOpen: !state.cartOpen })),
}))

useCartStore hanya menyimpan productIds dan cartOpen — bukan data produk itu sendiri. addProduct dan toggleCart memutasi UI state murni. Data detail produk tetap diambil lewat useQuery di komponen.

Komponen Menggabungkan Keduanya

Komponen memakai kedua sumber tanpa konflik: store untuk kontrol UI, query untuk data:

JSMenggabungkan store dan query
function CartButton() {
  const { productIds, cartOpen, toggleCart } = useCartStore()
  const { data: products } = useQuery({
    queryKey: ["products"],
    queryFn: fetchProducts,
  })
 
  const selected = products?.filter((p) => productIds.includes(p.id))
 
  return (
    <button onClick={toggleCart}>
      Keranjang ({selected?.length ?? 0}) {cartOpen ? "tutup" : "buka"}
    </button>
  )
}

productIds dari store dan products dari query digabung hanya saat render. selected dihitung dari id yang disimpan di store dan data lengkap dari cache — tidak ada duplikasi, karena store tidak pernah menyimpan objek produk.

Cache sebagai Single Source of Truth

getQueryData untuk Membaca Cache

Kadang komponen butuh data yang sudah ada di cache tanpa membuat query baru — misalnya setelah mutasi, atau untuk logika non-render. getQueryData adalah pintunya:

JSMembaca data dari cache
import { useQueryClient } from "@tanstack/react-query"
 
function TodoSummary() {
  const queryClient = useQueryClient()
 
  const todos = queryClient.getQueryData(["todos"]) ?? []
 
  const done = todos.filter((t) => t.completed).length
  return <p>{done} dari {todos.length} todo selesai</p>
}

queryClient.getQueryData(["todos"]) membaca cache tanpa fetch dan tanpa render ulang otomatis. getQueryData mengembalikan data apa adanya — jika ingin berlangganan perubahan, gunakan useQuery biasa.

Menghindari Duplikasi Data

Aturan emasnya: jangan pernah menyalin data server ke store. Kalau produk yang sama disimpan di cache dan di store, keduanya bisa tidak sinkron, dan invalidasi di TanStack Query tidak akan menyentuh salinan di store. Data server hidup di satu tempat saja: cache TanStack Query.

Memperbarui Cache dari Mutasi

Setelah mutasi, perbarui cache dengan setQueryData (episode 6 dan 11) sehingga semua komponen yang membaca query tersebut langsung melihat data baru — tanpa perantara store:

JSUpdate cache, bukan store
const mutation = useMutation({
  mutationFn: createTodo,
  onSuccess: (newTodo) => {
    queryClient.setQueryData(["todos"], (old) => [...(old ?? []), newTodo])
  },
})

setQueryData(["todos"], ...) menulis langsung ke satu-satunya sumber kebenaran. Semua komponen yang memakai ["todos"] akan ikut ter-update — inilah kekuatan cache sebagai single source of truth.

Warning

Hati-hati menaruh seluruh objek dari query ke dalam store untuk "kemudahan akses". Itu menciptakan salinan kedua yang mudah tidak sinkron. Simpan id, bukan objek, dan biarkan query yang mengambil data.

Kapan Tetap Butuh Store

Meskipun pola hybrid menyarankan TanStack Query untuk server state, ada kasus store tetap diperlukan: state yang benar-benar global dan cepat berubah seperti theme, sidebar state, form multi-langkah, dan keranjang yang perlu dibagikan antar halaman. Intinya bukan menghapus store, tapi membatasi store hanya pada domain UI.

Penutup

Episode 16 merangkum pola arsitektur modern: TanStack Query untuk server state, Zustand atau Redux untuk UI state, dan cache yang diperlakukan sebagai single source of truth lewat getQueryData dan setQueryData.

Inti yang harus dibawa pulang:

  • TanStack Query menangani server state; store menangani UI state.
  • Simpan id di store, bukan objek data dari server.
  • getQueryData membaca cache tanpa fetch atau render ulang.
  • Jangan menyalin data server ke store untuk mencegah duplikasi.
  • Perbarui cache dengan setQueryData setelah mutasi.
  • Store tetap berguna untuk state global yang cepat berubah.

Di episode 17 selanjutnya kita akan membahas devtools dan debugging — memantau cache dan status query dengan React Query Devtools, serta troubleshooting masalah umum seperti query yang tidak refetch, infinite loop, error serialization, dan memory leak cache.

Belajar TanStack Query - Integrasi dengan State Management | Belajar TanStack Query