Belajar TanStack - Full Application Patterns
Episode 15 of 24

Belajar TanStack - Full Application Patterns

Episode ini menyatukan seluruh library: dashboard dengan TanStack Table dan Charts, koordinasi query state, table state, dan router state, complex UI flows dengan nested routing, serta data-driven UI dan composition.

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

Pendahuluan

Empat belas episode membangun kemampuan per library. Sekarang saatnya menyatukan semuanya: Query menyediakan data, Table menyajikannya, Charts memvisualisasikannya, dan Router mengatur navigasi. Episode 15 membangun pola aplikasi penuh yang menjadi cetak biru proyek kalian.

Episode 15 membahas dashboard dengan Table dan Charts, koordinasi query state, table state, dan router state, complex UI flows dengan nested routing, serta data-driven UI dan composition.

Di sinilah filosofi TanStack membayar lunas: karena tiap library headless dan independen, mengombinasikannya adalah soal menghubungkan state, bukan melawan API.

Dashboard dengan TanStack Table dan Charts

Satu Sumber Data, Banyak Tampilan

Dashboard membaca satu kumpulan data, lalu menampilkannya sebagai tabel dan chart. Sumber datanya satu query; keduanya tinggal mengkonsumsi cache yang sama:

JSDashboard dengan table dan chart
import { Chart } from "@tanstack/react-charts"
import { useQuery } from "@tanstack/react-query"
 
function Dashboard() {
  const { data } = useQuery({
    queryKey: ["penjualan"],
    queryFn: ambilPenjualan,
  })
 
  const chartData = [
    {
      id: "penjualan",
      label: "Penjualan per Bulan",
      data: data.points,
    },
  ]
 
  return (
    <div>
      <Chart options={{
        data: chartData,
        primaryAxis: { getValue: (p) => p.bulan },
        secondaryAxes: [{ getValue: (p) => p.total }],
      }} />
      <TabelPenjualan rows={data.rows} />
    </div>
  )
}

useQuery mengambil penjualan sekali. Data yang sama diubah bentuk untuk Chart dan diteruskan ke tabel. Karena cache sama, memodifikasi data di tabel langsung tercermin di chart — tanpa sinkronisasi manual.

Koordinasi State Query, Table, dan Router

Satu URL untuk Seluruh Kondisi

Filter, halaman, dan pencarian tabel paling baik di-simpan di URL. Router menyediakan search params, dan query memakai search params sebagai bagian dari queryKey:

JSSearch params sebagai filter tabel
const filterRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: "laporan",
  validateSearch: (search) => ({
    halaman: search.halaman ?? 1,
    cari: search.cari ?? "",
  }),
  loader: async ({ context, search }) => {
    return context.queryClient.ensureQueryData({
      queryKey: ["laporan", search],
      queryFn: () => ambilLaporan(search),
    })
  },
  component: LaporanComponent,
})

validateSearch mendefinisikan bentuk search params dengan default. Loader memakai search sebagai bagian dari queryKey, sehingga setiap kombinasi filter dan halaman punya cache sendiri. Tabel kemudian membaca search ini untuk mengisi filter dan pagination — URL menjadi single source of truth.

Complex UI Flows dengan Nested Routing

Layout Berlapis untuk Alur Kompleks

Nested routing memecah flow yang rumit menjadi route kecil yang masing-masing punya loader dan error handling sendiri:

JSNested routes untuk flow bertingkat
const checkoutRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: "checkout",
  component: LayoutCheckout,
})
 
const alamatRoute = createRoute({
  getParentRoute: () => checkoutRoute,
  path: "alamat",
  component: FormAlamat,
})
 
const pembayaranRoute = createRoute({
  getParentRoute: () => checkoutRoute,
  path: "pembayaran",
  loader: ({ context }) =>
    context.queryClient.ensureQueryData({ queryKey: ["metode"], queryFn: ambilMetodePembayaran }),
  component: FormPembayaran,
})

alamatRoute dan pembayaranRoute berinduk pada checkoutRoute. Layout checkout berisi stepper dan <Outlet />, sementara tiap langkah memuat datanya sendiri. Navigasi antar langkah mengubah URL dan memicu loader per langkah — alur yang panjang jadi terukur.

Data-driven UI dan Composition

UI yang Dibangun dari Data

Pola terakhir: biarkan data menentukan struktur UI. Table membaca definisi kolom sebagai data, chart membaca series sebagai data, router membaca route tree sebagai data. Semuanya composition:

JSKomposisi data-driven
function HalamanTabel({ definisiKolom, rows }) {
  const table = useReactTable({
    data: rows,
    columns: definisiKolom,
    getCoreRowModel: getCoreRowModel(),
  })
 
  return <RenderTable table={table} />
}

definisiKolom diteruskan sebagai prop dari data — bukan hard-coded di dalam komponen. Dengan cara ini, halaman yang berbeda bisa memakai komponen tabel yang sama dengan kolom berbeda. Ini esensi data-driven UI: logika dan tampilan dipisahkan, data menjadi penggerak keduanya.

Info

Saat memegang search params di URL, jangan lupa batasi nilainya di validateSearch. Ini mencegah nilai tak terduga masuk ke queryKey dan merusak cache.

Penutup

Episode 15 menutup pola aplikasi penuh: dashboard yang menyatukan Query, Table, dan Charts, state terkoordinasi lewat search params di URL, flow kompleks dengan nested routing, dan komposisi data-driven yang membuat UI fleksibel.

Inti yang harus dibawa pulang:

  • Satu query bisa memberi makan tabel dan chart sekaligus.
  • Search params di URL menjadi single source of truth filter.
  • queryKey menyertakan search agar cache terpisah per kondisi.
  • Nested routing memecah flow kompleks menjadi route kecil.
  • Loader per langkah menjaga tiap tahap punya data sendiri.
  • Komposisi data-driven memisahkan logika dari tampilan.

Di episode 16 selanjutnya kita akan membahas testing dan quality assurance — unit testing hooks Query, testing navigasi Router, testing render tabel dan list virtual, serta integration testing dengan mock server. Aplikasi kalian akan mulai diuji dengan benar!

Belajar TanStack - Full Application Patterns | Belajar TanStack