Belajar Zustand - Optimasi & Debugging
Episode 19 of 23

Belajar Zustand - Optimasi & Debugging

Episode ini membahas cara sistematis menemukan masalah performa dan bug di Zustand: profil re-render dengan React Profiler dan Redux DevTools, lalu troubleshooting umum seperti infinite render, state yang tidak ter-update, hydration mismatch, dan stale closure pada subscription.

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

Pendahuluan

Ketika aplikasi terasa lambat atau berperilaku aneh, kalian butuh cara sistematis menemukan akar masalahnya. Episode 19 membahas dua alat utama — React Profiler dan Redux DevTools — untuk melacak re-render dan update store, lalu menelusuri masalah yang paling sering muncul di Zustand: infinite render, state yang tidak ter-update, hydration mismatch, dan stale closure.

Setiap masalah kita bahas gejala, penyebab, dan solusinya supaya debugging tidak lagi menebak-nebak. Pola yang dipakai selalu sama: ukur dulu, lalu perbaiki berdasarkan data, bukan asumsi.

Profil Re-render

React Profiler

React Profiler di DevTools browser merekam render tiap komponen beserta durasinya. Jalankan aplikasi, lakukan interaksi, lalu periksa komponen mana yang render paling sering dan paling lambat. Store Zustand muncul sebagai hook di dalam komponen pemakainya — perhatikan komponen dengan selector yang mengembalikan object baru.

Untuk mengaktifkan profiling di production, render aplikasi dengan React profiling build:

Profiling build Next.js
npm run build -- --profile

Perintah npm run build -- --profile menghasilkan production build dengan React Profiler aktif, berguna untuk mengukur performa nyata tanpa mode development.

Setelah build berjalan, buka React Profiler di DevTools dan rekam satu sesi interaksi. Filter komponen dengan total render tertinggi, lalu periksa penyebabnya satu per satu: props baru, state baru, atau re-render induk. Data ini jauh lebih akurat daripada menebak komponen mana yang bermasalah.

Redux DevTools untuk Update Store

Dengan middleware devtools, setiap set muncul sebagai action di Redux DevTools. Panel diff memperlihatkan field yang berubah, dan tombol time-travel memundurkan state ke sebelum bug terjadi. Ini cara tercepat membuktikan bahwa update store adalah penyebab re-render beruntun.

Kombinasikan keduanya: React Profiler menunjukkan komponen mana yang render berlebihan, sementara Redux DevTools menunjukkan update store mana yang memicunya. Dengan dua sudut pandang ini, akar masalah hampir selalu terlihat.

Troubleshooting: Infinite Render

Gejala paling umum: komponen me-render terus-menerus sampai aplikasi hang. Penyebabnya selector yang mengembalikan object atau array baru setiap render:

JSPenyebab infinite render
const { count } = useCounter((s) => ({ count: s.count }))

(s) => ({ count: s.count }) membuat object baru setiap render, sehingga strict equality selalu gagal dan React me-render lagi. Solusinya pakai selector primitif atau useShallow. Kalau sudah pakai useShallow dan masih hang, periksa apakah action memicu set yang mengubah state yang sama di dalam render loop.## Troubleshooting: State Tidak Ter-update

Gejala kedua: UI tidak berubah meski set sudah dipanggil. Penyebabnya biasanya mutasi state secara langsung sehingga referensi object tidak berubah. Zustand membandingkan dengan strict equality — object yang di-mutasi tetap object yang sama:

JSMutasi yang tidak terdeteksi
const setProfile = (name) =>
  set((s) => {
    s.user.name = name
    return s
  })

s.user.name = name mengubah properti tanpa membuat object baru, sehingga React tidak melihat perubahan. Solusinya selalu kembalikan object baru:

JSUpdate immutable yang benar
const setProfile = (name) =>
  set((s) => ({ user: { ...s.user, name } }))

{ ...s.user, name } menghasilkan referensi baru yang dideteksi strict equality. Bila object sangat bersarang, pertimbangkan middleware immer dari episode 9 untuk menulis draft yang mutable.

Untuk memverifikasi bahwa update terdeteksi, bandingkan referensi state sebelum dan sesudah set: getState().user harus menghasilkan object yang berbeda setelah set berhasil.

Hydration Mismatch dan Stale Closure

Hydration Mismatch

Error ini muncul saat render server berbeda dengan render client pertama — biasanya karena store membaca localStorage saat render. Solusinya sudah dibahas di episode 14: gunakan hook useHydrated dan jangan akses storage saat render server.

Jika error muncul di browser namun tidak di server, periksa juga apakah komponen menulis state saat render. Menulis state ke store di dalam render function memicu update yang tidak sinkron dan berpotensi mismatch — pindahkan penulisan ke dalam event handler atau effect.

Stale Closure pada Subscription

Subscription yang menangkap variabel lama dari closure memakai nilai usang:

JSSubscription dengan stale closure
useEffect(() => {
  return useAuthStore.subscribe((state) => {
    if (state.token && state.user.role === 'admin') {
      enableAdminPanel()
    }
  })
}, [])

state.token dan state.user.role dibaca dari parameter subscribe, bukan dari closure luar, sehingga selalu terbaru. Aturan emas: di dalam listener, baca nilai dari state yang diteruskan subscribe, bukan dari variabel luar yang mungkin basi.

Alternatifnya, panggil getState di dalam listener agar selalu membaca nilai terbaru tanpa bergantung pada parameter:

JSBaca nilai terbaru via getState
useEffect(() => {
  return useAuthStore.subscribe(() => {
    const { token, user } = useAuthStore.getState()
    if (token && user.role === 'admin') {
      enableAdminPanel()
    }
  })
}, [])

Contoh umum stale closure lainnya: menyimpan token dari render pertama ke variabel luar, lalu membacanya di dalam listener. Karena listener menangkap variabel lama, nilainya tidak pernah terbaru. Solusinya selalu baca dari parameter subscribe atau panggil getState di dalam listener.

Penutup

Episode 19 melengkapi kotak alat debugging: React Profiler untuk merekam re-render, Redux DevTools untuk melacak update store, plus solusi untuk empat masalah umum — infinite render dari object baru, state yang tidak ter-update dari mutasi, hydration mismatch, dan stale closure pada subscription.

Sisakan waktu untuk memahami root cause daripada sekadar menimpa gejala — perbaikan yang benar muncul dari pengukuran yang benar.

Inti yang harus dibawa pulang:

  • React Profiler merekam render dan durasi tiap komponen.
  • Redux DevTools menampilkan tiap set sebagai action dengan diff.
  • Object baru di dalam selector menyebabkan infinite render.
  • Mutasi langsung tidak terdeteksi strict equality.
  • Hydration mismatch dicegah dengan menahan render sampai hydrated.
  • Baca state dari parameter listener, bukan dari closure luar.

Di episode 20 selanjutnya kita akan membahas fitur stabil terbaru Zustand v5 — fungsi useStore untuk React 19, perbaikan typing middleware, rangkaian rilis 2026 dari v5.0.10 sampai v5.0.14, dan cara memverifikasi versi terbaru di registry.

Belajar Zustand - Optimasi & Debugging | Belajar Zustand