Belajar SvelteKit - Stores & State Management
Episode 7 of 24

Belajar SvelteKit - Stores & State Management

Episode ini membahas state management di SvelteKit: writable, readable, dan derived stores, custom stores untuk shared state, sinkronisasi state antara server dan client, serta integrasi dengan TanStack Query untuk server state. Kalian akan memilih strategi state yang tepat untuk setiap kebutuhan.

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

Pendahuluan

Seiring aplikasi tumbuh, state tidak lagi hanya milik satu komponen. Keranjang belanja yang dilihat header, navbar, dan halaman keranjang adalah contoh state yang harus dibagikan. Episode 7 membahas store — mekanisme resmi Svelte untuk shared state.

Selain store, kita juga perlu memikirkan jenis state yang berbeda. State lokal komponen ditangani runes, state bersama client memakai store, dan server state yang datang dari API punya siklus hidupnya sendiri — sering kali lebih baik ditangani pustaka seperti TanStack Query.

Di akhir episode ini kalian akan tahu kapan memakai runes, kapan memakai store, dan kapan menyerahkan state ke pustaka query. Pilihan yang tepat menghindarkan aplikasi dari bug sinkronisasi yang sulit dilacak.

Writable, Readable, dan Derived Stores

Writable Store

Writable store adalah store paling dasar: nilainya bisa dibaca, diubah, dan dihitung ulang. Buat dengan fungsi writable dari modul svelte/store.

JSWritable dan derived store
import { writable, derived, get } from "svelte/store";
 
export const hitung = writable(0);
export const duaKali = derived(hitung, ($hitung) => $hitung * 2);
 
hitung.set(5);
hitung.update((n) => n + 1);
console.log(get(hitung));

set mengganti nilai, update mengubah nilai berdasarkan nilai sebelumnya, dan get membaca nilai satu kali untuk konteks non-komponen. Semua operasi ini diikuti pemberitahuan otomatis ke seluruh subscriber.

Derived Store

Derived store menghitung nilai baru dari satu atau beberapa store lain. Mirip $derived di komponen, tapi bersifat global dan bisa digunakan di mana saja, termasuk di dalam modul.

JSDerived dari banyak store
import { derived } from "svelte/store";
 
export const total = derived([keranjang, ongkir], ([items, biaya]) => {
    const subtotal = items.reduce((jumlah, item) => jumlah + item.harga, 0);
    return subtotal + biaya;
});

Derived store hanya menghitung ulang saat dependensinya berubah. Untuk store yang nilainya tidak pernah berubah — misalnya daftar fitur yang diaktifkan — gunakan readable store. Readable cocok untuk nilai yang dibaca banyak komponen tetapi di-set sekali, seperti data konfigurasi yang diisi dari luar.

Custom Stores dan Shared State

Membungkus Store dengan Logika

Custom store menggabungkan subscribe dengan method khusus. Ini adalah pola terbaik untuk state domain seperti keranjang atau daftar notifikasi.

JSCustom store keranjang
import { writable } from "svelte/store";
 
function buatKeranjang() {
    const { subscribe, update, set } = writable([]);
 
    return {
        subscribe,
        tambah: (produk) => update((items) => [...items, produk]),
        hapus: (id) => update((items) => items.filter((p) => p.id !== id)),
        kosongkan: () => set([])
    };
}
 
export const keranjang = buatKeranjang();

Method tambah dan hapus menyembunyikan detail update dari pemakai. Komponen cukup memanggil keranjang.tambah(produk) tanpa tahu bagaimana array diubah.

Menggunakan Store di Komponen

Di dalam komponen, tambahkan prefix $ pada nama store untuk auto-subscription:

Auto-subscription dengan prefix dolar
<script>
    import { hitung } from "$lib/stores/hitung";
    import { keranjang } from "$lib/stores/keranjang";
</script>
 
<p>Hitungan: {$hitung}</p>
<p>Jumlah item: {keranjang.length}</p>
<button onclick={() => hitung.update((n) => n + 1)}>Naik</button>
<button onclick={() => keranjang.tambah({ id: 1, harga: 5000 })}>Tambah</button>

Sintaks $store otomatis subscribe saat komponen dibuat dan unsubscribe saat dihancurkan. Ini menghindari kebocoran memori yang umum terjadi jika kalian menulis subscribe manual.

State Hydration antara Server dan Client

Masalah State di SSR

Store adalah singleton di dalam bundle JavaScript. Saat SSR, satu store yang sama digunakan untuk semua request — ini berbahaya karena state dari request satu bisa bocor ke request lain. Jangan pernah mengisi store global dengan data yang spesifik per pengguna di dalam load function.

Pola yang Benar

Alur yang aman adalah: load function mengembalikan data sebagai prop, komponen root menginisialisasi store dari data tersebut, dan setiap request mendapat snapshot sendiri. Untuk state per-request yang tidak perlu diserahkan ke seluruh halaman, gunakan setContext di layout root — context tidak bocor antar request.

Inisialisasi store dari data load
<script>
    import { page } from "$app/stores";
    import { keranjang } from "$lib/stores/keranjang";
 
    let { data } = $props();
    keranjang.set(data.keranjangAwal);
</script>
 
<p>Selamat datang, {data.user.nama}</p>

Server state seperti user session dan data keranjang sebaiknya selalu berasal dari server dan ditampilkan sebagai data, bukan diunduh ulang oleh store di client.

Integrasi dengan TanStack Query

Server State yang Terpisah

Store paling cocok untuk client state: tema, filter, form draft. Untuk server state — data yang dimiliki backend — TanStack Query (versi Svelte) menyediakan caching, retry, dan invalidation yang tidak perlu kalian bangun sendiri.

Query dengan TanStack Query
<script>
    import { createQuery } from "@tanstack/svelte-query";
 
    const artikel = createQuery({
        queryKey: ["artikel"],
        queryFn: () => fetch("/api/artikel").then((r) => r.json())
    });
</script>
 
{#if artikel.isPending}
    <p>Memuat...</p>
{:else if artikel.isError}
    <p>Gagal memuat: {artikel.error.message}</p>
{:else}
    <ul>
        {#each artikel.data as item}
            <li>{item.judul}</li>
        {/each}
    </ul>
{/if}

createQuery menangani status pending, error, dan success sekaligus. Data di-cache berdasarkan queryKey dan bisa di-invalidate setelah mutasi, sehingga UI selalu sinkron dengan server.

Pembagian Tanggung Jawab

Pola yang matang membagi state secara jelas: load functions untuk data awal halaman, TanStack Query untuk data yang berubah dan di-fetch berulang, store untuk UI state bersama, dan runes untuk state lokal. Tanpa pembagian ini, aplikasi berisiko punya sumber kebenaran ganda yang saling bertabrakan.

Penutup

Inti yang harus dibawa pulang:

  • writable, readable, dan derived adalah tiga tipe store dasar di modul svelte/store.
  • Custom store menggabungkan subscribe dengan method domain, menyembunyikan detail mutasi.
  • Prefiks $ di komponen melakukan auto-subscription dan cleanup otomatis.
  • Store adalah singleton; jangan mengisinya dengan data per-request di SSR untuk mencegah kebocoran state.
  • Data dari server sebaiknya melewati load functions dan diteruskan sebagai props.
  • TanStack Query menangani server state dengan caching dan invalidation; store untuk client state.

Di episode 8 selanjutnya kita membahas configuration & runtime config: svelte.config.js dan runtime configuration, environment variables publik dan privat, adapters dan deployment targets, serta feature flags untuk build environment yang berbeda.

Belajar SvelteKit - Stores & State Management | Belajar SvelteKit