Belajar Zustand - Persistence & Rehydration Lanjutan
Episode 11 of 23

Belajar Zustand - Persistence & Rehydration Lanjutan

Episode ini membahas persistence dan rehydration lanjutan: custom storage dengan createJSONStorage untuk sessionStorage dan backend, serialization dan deserialization, serta versioning dengan migrate untuk memigrasikan state lama. Kalian juga belajar menangani state yang gagal ter-rehydrate lewat callback onRehydrateStorage.

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

Pendahuluan

Episode 8 memperkenalkan middleware persist dengan localStorage bawaan. Episode 11 membawanya lebih jauh: bagaimana menyimpan state ke storage yang bukan localStorage, bagaimana mengontrol proses serialization, dan bagaimana memigrasikan state lama ketika bentuk data berubah seiring versi aplikasi. Ini adalah bekal yang wajib kalian miliki sebelum aplikasi dinaikkan ke production.

Tiga topik utama yang kita bedah: custom storage dengan createJSONStorage untuk sessionStorage hingga endpoint backend, opsi version plus migrate untuk state yang berubah skema, dan callback onRehydrateStorage untuk menangani kegagalan saat state dimuat ulang dari penyimpanan.

Custom Storage dengan createJSONStorage

Storage Bawaan dan Kustom

Secara default, persist memakai localStorage. Untuk mengubah tujuan penyimpanan, createJSONStorage menghasilkan adapter storage yang memakai getItem, setItem, dan removeItem standar:

JSPersist ke sessionStorage
import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'
 
interface SessionState {
  token: string
  setToken: (token: string) => void
}
 
export const useSession = create<SessionState>()(
  persist(
    (set) => ({
      token: '',
      setToken: (token) => set({ token }),
    }),
    {
      name: 'session-store',
      storage: createJSONStorage(() => sessionStorage),
    },
  ),
)

createJSONStorage(() => sessionStorage) menerima fungsi yang mengembalikan objek Storage. Mengapa fungsi, bukan objek langsung? Karena di lingkungan seperti server, sessionStorage tidak tersedia saat modul dimuat — dengan fungsi, akses ditunda sampai benar-benar dibutuhkan.

Storage Backend atau Asinkron

Storage tidak harus berupa penyimpanan browser. Zustand mendukung storage asinkron apa pun yang mengimplementasikan kontrak getItem, setItem, dan removeItem. Untuk menyimpan state ke API backend:

JSStorage kustom ke endpoint API
const apiStorage = {
  getItem: async (name: string) => {
    const res = await fetch(`/api/storage/${name}`)
    return res.json()
  },
  setItem: async (name: string, value: string) => {
    await fetch(`/api/storage/${name}`, {
      method: 'POST',
      body: value,
    })
  },
  removeItem: async (name: string) => {
    await fetch(`/api/storage/${name}`, { method: 'DELETE' })
  },
}

Serialization dan Deserialization

Persist menyimpan state sebagai string JSON. Secara default, hanya nilai yang didukung JSON yang ikut tersimpan — fungsi seperti action di dalam store otomatis dihilangkan. Untuk format non-JSON, kalian bisa menimpa perilaku default lewat opsi serialize dan deserialize:

JSSerialization kustom
persist(initializer, {
  name: 'cart',
  storage: createJSONStorage(() => localStorage, {
    reviver: (key, value) => (key === 'total' ? Number(value) : value),
    replacer: (key, value) => (key === 'total' ? value.toFixed(2) : value),
  }),
})

replacer dan reviver mengikuti kontrak JSON.stringify dan JSON.parse. Gunakan ini untuk mengonversi tipe khusus seperti Date atau BigInt yang tidak didukung JSON asli. Untuk kebutuhan di luar JSON, tulis storage kustom yang mengembalikan string mentah.

Versioning dan Migrasi State

Opsi version

Skema state selalu bisa berubah. Ketika kalian menambah field baru atau mengubah bentuk field lama, state yang sudah tersimpan di storage pengguna menjadi usang. Opsi version menandai skema state:

JSMenandai versi skema state
export const useSettings = create<SettingsState>()(
  persist(
    (set) => ({
      theme: 'light',
      setTheme: (theme) => set({ theme }),
    }),
    { name: 'app-settings', version: 2 },
  ),
)

version: 2 memberi tahu persist bahwa state hanya valid untuk skema versi 2. Ketika kalian menaikkan angka ini, persist tahu bahwa state lama perlu diproses sebelum dipakai.

Fungsi migrate

Menyandingkan version dengan migrate untuk mengonversi state lama ke bentuk baru:

JSMigrasi state dari versi lama
migrate: (persisted, version) => {
  if (version === 0) {
    return { ...persisted, theme: 'light', fontSize: 14 }
  }
  if (version === 1) {
    return { ...persisted, fontSize: 14 }
  }
  return persisted
},

migrate(persisted, version) menerima state yang terbaca dan nomor versinya. Fungsi ini berjalan sekali saat rehydrate, lalu hasilnya menjadi initial state store. Tanpa migrate, state yang versinya tidak dikenal akan diabaikan.

Menangani Kegagalan Rehydration

Proses memuat ulang state dari storage disebut rehydration. Untuk storage asinkron, proses ini bisa gagal atau selesai setelah render pertama. Callback onRehydrateStorage memberi kalian titik masuk untuk menanganinya:

JSCallback onRehydrateStorage
onRehydrateStorage: () => (state, error) => {
  if (error) {
    console.error('Rehydration gagal', error)
    return
  }
  console.log('State dimuat dari storage', state)
},

onRehydrateStorage: () => (state, error) => ... dipanggil saat proses rehydrate selesai. Ini tempat yang tepat untuk logging, sinkronisasi state ke analitik, atau menampilkan notifikasi ketika state pengguna dipulihkan. Kombinasikan dengan pola skipHydration dari episode 8 untuk kontrol penuh atas waktu rehydration.

Penutup

Episode 11 menutup lapisan persistence lanjutan: storage di luar localStorage lewat createJSONStorage, serialization kustom untuk tipe non-JSON, versioning dengan migrate untuk skema yang berubah, dan callback onRehydrateStorage untuk kegagalan rehydrate.

Inti yang harus dibawa pulang:

  • createJSONStorage membungkus storage apa pun dengan kontrak getItem, setItem, dan removeItem.
  • Storage bisa asinkron, misalnya endpoint API backend.
  • Opsi version menandai skema state dan memicu migrate.
  • migrate mengonversi state lama ke bentuk terbaru saat rehydrate.
  • onRehydrateStorage menangani hasil sukses maupun error rehydration.
  • Action tidak ikut tersimpan karena persist menulis nilai JSON saja.

Di episode 12 selanjutnya kita akan membahas server state dan integrasi data — membedakan data API dari state UI, kapan data masuk ke Zustand dan kapan ke TanStack Query, serta pola hybrid auth dan user profile.

Belajar Zustand - Persistence & Rehydration Lanjutan | Belajar Zustand