Belajar Zustand - Middleware: immer
Episode 9 of 23

Belajar Zustand - Middleware: immer

Episode ini membahas middleware immer untuk memperbarui state nested secara mutable-tapi-immutable lewat fungsi draft, perbandingannya dengan manual spread, kapan memakai immer versus spread, serta trade-off performa keduanya.

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

Pendahuluan

State aplikasi nyata tidak pernah sederhana — object bersarang, array dalam object, dan struktur yang saling terkait. Di episode 4 kita belajar memperbarui state nested dengan spread berlapis yang rawan salah. Episode 9 memperkenalkan immer, middleware yang membiarkan kalian menulis mutasi langsung pada draft dan tetap menghasilkan state immutable.

Kita akan membahas cara kerja draft, pola penulisan yang benar, kapan immer mengungguli spread manual, dan kapan sebaiknya tidak memakainya.

Middleware immer

Update State Nested tanpa Spread Berlapis

Impor dan bungkus store dengan immer:

JSStore dengan middleware immer
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
 
interface ProfileState {
  profile: {
    name: string
    address: { city: string; country: string }
  }
  setCity: (city: string) => void
}
 
export const useProfile = create<ProfileState>()(
  immer((set) => ({
    profile: { name: 'Arman', address: { city: 'Bandung', country: 'ID' } },
    setCity: (city) =>
      set((state) => {
        state.profile.address.city = city
      }),
  })),
)

Di dalam set((state) => ...), parameter state adalah draft. Kalian memutasinya langsung — state.profile.address.city = city — dan immer menyalin hanya bagian yang berubah secara otomatis. Hasil akhirnya tetap immutable.

Fungsi Draft

Draft bekerja seperti struktur asli: kalian bisa memodifikasi properti, push ke array, atau hapus elemen, semuanya seolah mutasi biasa:

JSMutasi array via draft
addTag: (tag) =>
  set((state) => {
    state.tags.push(tag)
  }),
removeTag: (tag) =>
  set((state) => {
    const idx = state.tags.indexOf(tag)
    if (idx !== -1) state.tags.splice(idx, 1)
  }),

state.tags.push(tag) dan state.tags.splice(idx, 1) adalah mutasi langsung yang aman — immer mencatatnya sebagai perubahan struktural dan menghasilkan state baru. Kode jadi jauh lebih pendek dibanding spread berlapis.

Immer vs Manual Spread

Perbandingan Langsung

Untuk satu level, keduanya mirip. Perbedaannya mencolok saat struktur dalam:

JSSpread manual vs immer
// Spread manual, empat level
set((s) => ({
  settings: {
    ...s.settings,
    theme: {
      ...s.settings.theme,
      accent: 'teal',
    },
  },
}))
 
// Immer, satu mutasi
set((s) => {
  s.settings.theme.accent = 'teal'
})

set((s) => { s.settings.theme.accent = 'teal' }) menggantikan empat lapis spread dengan satu baris mutasi draft. Semakin dalam struktur, semakin besar keuntungan keterbacaan immer.

Risk Spread yang Salah

Kesalahan paling umum spread manual: lupa menyebar satu level, sehingga object lama masih ter-referensi dan perubahan tidak terdeteksi. Immer menghilangkan kelas error ini karena penyalinan ditangani otomatis.

Trade-off Performa

Overhead Immer

Immer bukan tanpa biaya. Setiap draft membutuhkan proxy dan proses copy-on-write, sehingga ada overhead kecil per update — sangat terasa saat state sangat besar dan update berfrekuensi tinggi, misalnya editor teks atau simulasi realtime.

Kapan immer terasa berat
Update kecil + state kecil   → spread manual sama cepat
Update dalam + state dalam   → immer lebih produktif
Update per frame, state besar → pertimbangkan spread

Untuk update per frame (animasi, drag), hindari immer. Untuk update user-triggered yang jarang dan state-nya dalam, immer adalah pilihan yang sangat nyaman.

Optimasi Minimal

Immer membiarkan update yang tidak mengubah apa pun — misalnya menetapkan nilai yang sama — dan hasilnya tetap mengembalikan referensi yang sama, sehingga tidak memicu re-render. Ini perilaku yang diinginkan.

Kapan Memakai Immer

Gunakan immer ketika:

  • State punya struktur object/array bersarang lebih dari dua level.
  • Banyak action melakukan update dalam yang rawan typo spread.
  • Tim lebih produktif dengan kode mutasi langsung yang jelas.

Hindari immer ketika:

  • State sederhana dan dangkal — spread cukup.
  • Update sangat sering dan state besar — overhead proxy kurang pantas.
  • Kalian ingin bundle sesekecil mungkin — immer menambah ukuran.
Install immer
npm i immer

npm i immer menginstall dependensi yang dibutuhkan middleware. Zustand mengekspos immer dari zustand/middleware/immer — versi package immer dipakai secara otomatis dari dependensi project kalian.

Penutup

Episode 9 menunjukkan kekuatan immer: update nested yang ditulis secara mutable-tapi-immutable lewat draft, kode yang jauh lebih pendek dari spread berlapis, dan pemilihan kapan immer layak dipakai berdasarkan kompleksitas state serta frekuensi update.

Inti yang harus dibawa pulang:

  • immer menghasilkan state immutable meski kalian menulis mutasi pada draft.
  • Parameter state di dalam set adalah draft yang bisa dimodifikasi langsung.
  • Push dan splice aman dipakai di draft.
  • Immer unggul di struktur dalam; spread cukup untuk state dangkal.
  • Ada overhead proxy per update — hindari untuk update per frame.
  • Install dengan npm i immer, impor dari zustand/middleware/immer.

Di episode 10 selanjutnya kita akan membahas middleware: devtools dan redux — integrasi Redux DevTools dengan time-travel dan tracing action, konfigurasi nama store, serta middleware redux untuk pola reducer ala Redux di dalam Zustand.