Belajar Zustand - Selector & Subscription
Episode 5 of 23

Belajar Zustand - Selector & Subscription

Episode ini membahas selector sebagai kunci re-render minimal, memakai useShallow dari zustand/react/shallow untuk memilih beberapa slice sekaligus, dan subscription manual dengan subscribeWithSelector untuk log, analitik, dan sinkronisasi di luar React.

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

Pendahuluan

Selector adalah jantung re-render selektif Zustand — kemampuan membaca sebagian state tanpa memicu render saat bagian lain berubah. Episode 5 membahasnya secara tuntas: pola selector tunggal, jebakan selector yang mengembalikan object baru, solusi useShallow, dan subscription manual untuk kebutuhan di luar React.

Kalian akan memahami kapan selector dipakai di hook dan kapan subscription manual lebih tepat. Ini juga fondasi performa yang akan kita optimalkan lebih lanjut di episode 13.

Selector: Memilih Slice State

Selector Tunggal

Bentuk paling sederhana dan paling efisien — satu nilai primitif per pemanggilan:

JSSelector nilai primitif
const count = useCounter((s) => s.count)
const name = useProfile((s) => s.profile.name)

useCounter((s) => s.count) mengembalikan primitif. Karena primitif dibandingkan dengan ===, komponen hanya re-render saat nilainya benar-benar berubah. Ini pola yang paling dianjurkan.

Jebakan Selector Object Baru

Jika selector mengembalikan object yang dibangun setiap render, hasilnya selalu dianggap berbeda:

JSSelector object baru yang salah
// BURUK: object baru setiap render menyebabkan infinite re-render
const { count, status } = useCounter((s) => ({ count: s.count, status: s.status }))

useCounter((s) => ({ count: s.count, status: s.status })) membuat object baru setiap kali dipanggil, sehingga strict equality selalu gagal dan komponen re-render tanpa henti. Solusinya: panggil selector terpisah, atau pakai useShallow.

useShallow

useShallow melakukan perbandingan dangkal — membandingkan setiap field satu per satu, bukan referensi object:

JSuseShallow untuk beberapa slice
import { useShallow } from 'zustand/react/shallow'
 
const { count, status } = useCounter(
  useShallow((s) => ({ count: s.count, status: s.status })),
)

useShallow((s) => ({ count: s.count, status: s.status })) diimpor dari zustand/react/shallow. Komponen re-render hanya ketika count atau status berubah. Catatan: impor di v5 berada di zustand/react/shallow, bukan lagi zustand/shallow.

useShallow vs Selector Terpisah

Pilih pendekatan
Selector terpisah → paling efisien, kode lebih panjang
useShallow      → nyaman, beberapa slice, perbandingan dangkal

Untuk dua hingga tiga slice, keduanya setara. Pilih useShallow saat jumlah slice mulai banyak dan kode selector terpisah terasa berulang.

Subscription Manual

subscribe dengan subscribeWithSelector

Secara default subscribe(listener) hanya menerima satu listener. Untuk subscribe dengan selector dan listener yang menerima dua argumen, aktifkan middleware subscribeWithSelector:

JSAktivasi subscribeWithSelector
import { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
 
export const useUserStore = create(
  subscribeWithSelector((set) => ({
    name: '',
    role: 'viewer',
    setName: (name) => set({ name }),
  })),
)

Dengan middleware aktif, subscribe(selector, listener) tersedia:

JSSubscribe dengan selector
const unsub = useUserStore.subscribe(
  (s) => s.role,
  (role, prevRole) => {
    console.log(`role berubah: ${prevRole}${role}`)
  },
)

useUserStore.subscribe((s) => s.role, listener) memanggil listener hanya ketika s.role berubah, dan fungsi unsub menghentikan subscription. Panggil unsub di cleanup useEffect agar tidak bocor.

Use Case: Log dan Analitik

Subscription manual cocok untuk efek samping global yang tidak memerlukan render:

JSLog perubahan state ke analitik
const unsub = useUserStore.subscribe(
  (s) => s.name,
  (name) => analytics.track('user_name_updated', { name }),
)

Pola ini dipakai untuk sinkronisasi native, pencatatan log, atau memanggil API saat slice tertentu berubah — semua tanpa memicu render komponen. analytics.track(...) di sini adalah placeholder layanan analitik kalian.

Pola Praktis Lainnya

Selector Reusable

Buat selector sebagai fungsi terpisah agar bisa diuji dan dipakai ulang:

JSSelector reusable
export const selectCount = (s: CounterState) => s.count
 
const count = useCounter(selectCount)

Selector bernama selectCount meningkatkan keterbacaan dan memudahkan unit testing — pola yang kembali dipakai di episode 18.

Penutup

Episode 5 mengunci pemahaman selector dan subscription: selector tunggal untuk re-render minimal, useShallow untuk beberapa slice dengan perbandingan dangkal, dan subscribeWithSelector untuk efek samping di luar React.

Inti yang harus dibawa pulang:

  • Selector primitif dibandingkan dengan ===, sehingga re-render tetap minimal.
  • Selector yang mengembalikan object baru setiap render memicu infinite re-render.
  • useShallow dari zustand/react/shallow membandingkan object secara dangkal.
  • subscribeWithSelector mengaktifkan subscribe(selector, listener).
  • Subscription manual cocok untuk log, analitik, dan sinkronisasi non-React.
  • Selalu panggil unsub di cleanup useEffect agar tidak bocor.

Di episode 6 selanjutnya kita akan membahas actions dan async state — menempatkan logika di dalam store, action async dengan async/await, pengelolaan status loading/error/success, dan error handling yang terpusat. State aplikasi nyata hampir selalu asinkron.

Belajar Zustand - Selector & Subscription | Belajar Zustand