Episode ini membahas cara mengontrol re-render: selector yang mengembalikan nilai primitif, useShallow untuk beberapa slice, dan menghindari object baru di dalam selector. Kalian juga belajar transient updates dengan subscribe dan ref untuk memperbarui nilai seperti progress bar tanpa memicu render.

Zustand cepat karena selektif, tapi kecepatan itu hilang jika selector ditulis asal. Episode 13 membahas cara mengukur dan mengontrol re-render: memilih slice state yang tepat, memakai useShallow untuk beberapa nilai, dan menghindari jebakan object baru yang membuat store terlihat selalu berubah. Kita juga membahas transient updates — memperbarui nilai seperti posisi mouse tanpa re-render sama sekali.
Dengan kombinasi selector yang benar dan transient updates, aplikasi tetap responsif meski state diperbarui puluhan kali per detik.
Zustand membandingkan hasil selector dengan strict equality. Perubahan state hanya memicu re-render jika nilai yang dikembalikan selector berbeda dari sebelumnya:
const count = useCounter((s) => s.count)
const name = useCounter((s) => s.user.name)useCounter((s) => s.count) mengembalikan nilai primitif sehingga perbandingan akurat. Selama count tidak berubah, komponen ini tidak re-render walau field lain dalam store berubah.
Untuk memilih beberapa nilai sekaligus tanpa re-render berlebihan, gunakan useShallow. Tanpa useShallow, object yang dikembalikan selector selalu baru sehingga komponen re-render terus-menerus:
import { useShallow } from 'zustand/react/shallow'
const { count, name } = useCounter(
useShallow((s) => ({ count: s.count, name: s.name })),
)useShallow((s) => ({ ... })) membandingkan tiap properti dengan Object.is. Object baru tetap dibuat, tapi re-render hanya terjadi jika nilai di dalamnya benar-benar berubah.
Selector yang mengembalikan object atau array baru setiap dipanggil adalah penyebab paling umum re-render tak berujung. Contoh yang bermasalah:
const user = useUser((s) => ({ name: s.name, age: s.age }))Walau name dan age tidak berubah, object literal baru diciptakan tiap render sehingga strict equality selalu gagal. Solusinya pakai useShallow, atau pilih nilai primitif secara terpisah. Aturan mudahnya: jangan pernah membuat object di dalam selector tanpa useShallow.
Tidak semua perubahan state perlu render. Posisi mouse, progress scroll, atau video playback bisa diperbarui puluhan kali per detik — me-render komponen untuk setiap frame adalah pemborosan. Zustand menyediakan subscribe untuk pola transient:
import { useEffect, useRef } from 'react'
import { useProgressStore } from './stores/progress'
function ProgressBar() {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
const unsubscribe = useProgressStore.subscribe(
(s) => s.progress,
(progress) => {
if (ref.current) {
ref.current.style.width = `${progress}%`
}
},
)
return unsubscribe
}, [])
return <div ref={ref} className="progress-bar" />
}useProgressStore.subscribe(selector, listener) mendaftarkan listener yang berjalan saat nilai progress berubah. Komponen hanya render satu kali; pembaruan berikutnya menulis langsung ke DOM via ref tanpa melalui React.
Selector getInitialState menyediakan nilai awal yang dikembalikan subscribe saat listener pertama kali dipanggil — berguna untuk inisialisasi ref:
useEffect(() => {
const unsubscribe = useProgressStore.subscribe(
(s) => s.progress,
(progress) => {
if (ref.current) {
ref.current.style.width = `${progress}%`
}
},
{ fireImmediately: true },
)
return unsubscribe
}, []){ fireImmediately: true } memanggil listener segera dengan nilai terkini. Cocok ketika komponen me-mount di tengah-tengah proses, bukan dari awal.
Episode 13 membahas senjata performa Zustand: selector yang memilih nilai kecil, useShallow untuk beberapa slice, menghindari object baru di dalam selector, dan transient updates yang memperbarui DOM tanpa render melalui subscribe plus ref.
Inti yang harus dibawa pulang:
Di episode 14 selanjutnya kita akan membahas SSR, React 19, dan integrasi framework — memakai Zustand dengan aman di Next.js App Router, menghindari perbedaan state saat hydration, integrasi hook use di React 19, serta store per-scope dengan Context.