Belajar Frontend - State Management & Data Fetching
Episode 11 of 28

Belajar Frontend - State Management & Data Fetching

Menata state aplikasi secara profesional: membedakan server state dan client state, memakai TanStack Query untuk data fetching yang robust (caching, retry, invalidation), serta Zustand atau Redux untuk state klien — dipraktikkan di keranjang TokoKita

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

Pendahuluan

Saat aplikasi TokoKita tumbuh, muncul masalah klasik: di mana data disimpan? useState per komponen mulai berantakan saat banyak komponen membutuhkan data yang sama. Episode 11 membedah state management & data fetching — topik yang paling sering salah dipahami pemula, karena jawaban yang benar bergantung pada jenis state yang sedang ditangani.

Kesalahan paling umum di 2026: memasukkan data server ke Redux/Zustand, lalu "menemukan ulang" caching yang seharusnya sudah disediakan library khusus. Memahami perbedaan server state vs client state akan menyelamatkan kalian dari ribuan baris boilerplate.

Server State vs Client State

Server StateClient State
AsalDatabase/API (di luar aplikasi)Aplikasi itu sendiri
ContohDaftar produk, harga, stokIsi form, tab aktif, tema, item keranjang
Sumber kebenaranServerKlien
SifatAsync, bisa gagal, bisa basiSinkron, langsung di memori
Library yang tepatTanStack QueryZustand / Redux Toolkit

Sumber kebenaran server state adalah server — klien hanya mencerminkannya. Karena itu solusinya bukan state manager, melainkan caching layer yang tahu kapan harus fetch, menyimpan, dan menyegarkan.

TanStack Query untuk Server State

TanStack Query (sebelumnya React Query) adalah standar de facto data fetching di React. Ia menyediakan caching, retry otomatis, refetch on focus, dan invalidasi — semua hal yang kalian akan "menemukan ulang" secara buruk jika menulis manual.

Query produk
import { useQuery } from "@tanstack/react-query";
 
export function useProducts() {
  return useQuery({
    queryKey: ["products"],
    queryFn: async () => {
      const res = await fetch("/api/products");
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      return res.json();
    },
  });
}

Penggunaannya deklaratif — komponen tidak peduli bagaimana data di-cache:

Memakai hasil query
export function Catalog() {
  const { data, isLoading, isError, error, refetch } = useProducts();
 
  if (isLoading) return <p>Memuat produk…</p>;
  if (isError) return <button onClick={() => refetch()}>Coba lagi</button>;
  return <ProductGrid products={data} />;
}

Tip

queryKey adalah "alamat cache": kunci unik yang menyatukan semua pemakai data yang sama. Jika kalian mengubah data (misal setelah checkout), panggil queryClient.invalidateQueries({ queryKey: ["products"] }) agar daftar produk otomatis diperbarui — tanpa menyentuh komponen yang membaca data itu.

Mutasi: Optimistic Update

Untuk aksi yang mengubah server (tambah ke keranjang, update profil), pakai useMutation dengan optimistic update — UI merespons instan sebelum server mengonfirmasi:

Mutation dengan optimistic update
const addToCart = useMutation({
  mutationFn: (productId: number) =>
    fetch("/api/cart", { method: "POST", body: JSON.stringify({ productId }) }),
  onMutate: (productId) => updateCartCache("+1"), // UI langsung berubah
  onError: () => rollbackCartCache(),             // gagal → kembalikan
});

Pola ini yang membuat UI terasa "seketika" — kunci pengalaman pengguna di episode 12.

Zustand dan Redux untuk Client State

Untuk state murni klien, pilih antara Zustand (minimalis, tanpa boilerplate) dan Redux Toolkit (terstruktur, banyak dipakai di perusahaan besar).

Store keranjang dengan Zustand
import { create } from "zustand";
 
interface CartState {
  items: CartItem[];
  add: (item: CartItem) => void;
  remove: (id: number) => void;
}
 
export const useCartStore = create<CartState>((set) => ({
  items: [],
  add: (item) => set((s) => ({ items: [...s.items, item] })),
  remove: (id) => set((s) => ({ items: s.items.filter((i) => i.productId !== id) })),
}));

Komponen apa pun membaca dan mengubah store yang sama:

Memakai store
import { useCartStore } from "@/store/cart";
 
export function CartButton() {
  const items = useCartStore((s) => s.items);
  return <button>Tambah ({items.length})</button>;
}

Zustand dipilih saat ingin cepat tanpa konsep tambahan; Redux Toolkit saat tim besar butuh struktur ketat (slices, devtools, middleware). Keduanya sah — pilih sesuai kebutuhan tim.

Praktik: Keranjang TokoKita yang Robust

Arsitektur state TokoKita yang disarankan:

  1. Server state (produk, harga) → useQuery(["products"]), cache di TanStack Query.
  2. Client state (item keranjang, kuantitas) → Zustand store.
  3. Aksi yang mengubah server (checkout) → useMutation + optimistic update.
  4. Setelah checkout berhasil → invalidate ["cart"] dan ["products"] (stok berkurang).
Peta state TokoKita
Server state: products, stock, cart (di DB)
  → TanStack Query: ["products"], ["cart"]
Client state: tampilan, form, filter, tema
  → Zustand: useCartStore (hanya transisi)

Common Pitfalls

KesalahanDampakPerbaikan
Menaruh server state di Redux/ZustandBoilerplate + cache basiTanStack Query
Fetch manual di tiap komponenDuplikasi & no cachingSatu queryFn per resource
Query tanpa queryKey stabilCache tidak terpakaiKey unik per resource
Mutasi tanpa error handlingData divergenonError + rollback
Store global untuk semuanyaKomponen re-render tak perluState di dekat pemakainya

Penutup

Inti yang harus dibawa pulang:

  • Bedakan server state (dari API) dan client state (dalam aplikasi) — solusinya berbeda.
  • TanStack Query untuk data: caching, retry, invalidasi, optimistic update — deklaratif dan robust.
  • Zustand untuk store ringan, Redux Toolkit untuk tim yang butuh struktur.
  • Pisahkan fetch dari komponen; jadikan queryFn satu-satunya sumber data.

Di episode 12 selanjutnya kita mengukur hasil kerja kita: web performance dengan Core Web Vitals — LCP, INP, CLS, code-splitting, lazy loading, dan profiling, agar TokoKita terasa instan di perangkat pengguna nyata. Sampai jumpa di episode 12!

Belajar Frontend - State Management & Data Fetching | Belajar Frontend