Episode ini membedah anatomi create((set, get) => ...), dua bentuk set berupa object dan fungsi updater, serta peran get untuk membaca state terkini di dalam action. Kalian juga belajar pola update yang benar dengan spread dan immutability.

Di episode 3 kalian menulis store pertama tanpa menyadari kekuatan penuh yang dipegang set dan get. Episode 4 membedah anatomi create(): apa saja yang diterima fungsi initializer, dua bentuk set, kapan memakai get, dan mengapa immutability menjadi hukum yang tidak bisa ditawar.
Setelah episode ini, kalian akan menulis action yang kompleks — membaca state lain, memperbarui beberapa field sekaligus, dan menghindari bug referensi — dengan percaya diri.
create() menerima fungsi initializer yang dipanggil sekali saat store dibuat:
import { create } from 'zustand'
type CartState = {
items: string[]
addItem: (item: string) => void
summary: () => number
}
export const useCart = create<CartState>((set, get) => ({
items: [],
addItem: (item) => set((s) => ({ items: [...s.items, item] })),
summary: () => get().items.length,
}))Parameter pertama set memperbarui state, parameter kedua get membaca state terkini. get().items.length di dalam summary membaca panjang array saat action dipanggil — bukan saat store dibuat.
Hasil create() adalah hook yang punya properti statis: .getState(), .setState(), dan .subscribe(). Store dan hook jadi satu, tanpa konfigurasi terpisah — inilah yang membuat API Zustand terasa seperti satu kesatuan.
Bentuk pertama menerima object parsial yang di-merge ke state:
reset: () => set({ count: 0, status: 'idle' })set({ count: 0, status: 'idle' }) mengganti field count dan status sekaligus, sedangkan field lain dipertahankan. Bentuk ini tepat ketika nilai baru sudah tersedia dan tidak bergantung pada state lama.
Bentuk kedua menerima fungsi yang menerima state terkini:
increment: () => set((s) => ({ count: s.count + 1 }))set((s) => ({ count: s.count + 1 })) aman dipanggil berkali-kali karena selalu membaca state terbaru. Jika beberapa set dipanggil beruntun, bentuk updater tidak akan kehilangan perubahan — beda dengan object langsung yang dihitung dari nilai lama.
get berguna ketika action butuh membaca state di luar field yang diubah, misalnya sebelum membuat keputusan:
export const useAuth = create<AuthState>((set, get) => ({
user: null,
login: (user) => {
const prev = get().user
set({ user, lastLogin: prev ? prev.name : null })
},
isLoggedIn: () => get().user !== null,
}))
if (useAuth.getState().isLoggedIn()) {
console.log('sudah login')
}get().user di dalam action membaca state terbaru, dan useAuth.getState().isLoggedIn() memungkinkan pemanggilan dari luar komponen — misalnya dari utility atau router guard.
Saat memperbarui state nested, kalian harus menyalin level yang diubah secara manual:
type ProfileState = {
profile: { name: string; email: string }
setEmail: (email: string) => void
}
export const useProfile = create<ProfileState>((set) => ({
profile: { name: 'Arman', email: '' },
setEmail: (email) =>
set((s) => ({ profile: { ...s.profile, email } })),
})){ ...s.profile, email } membuat object profile baru sambil mempertahankan field lain. Memutasi s.profile.email = email tanpa salinan akan merusak deteksi perubahan — ini topik inti immer di episode 9.
Panggil set beberapa kali dalam satu action jika langkah update berbeda, tapi ingat: setiap set memicu notifikasi.
export const useForm = create<FormState>((set) => ({
values: {},
errors: {},
submitStart: () => {
set({ status: 'loading' })
set({ errors: {} })
},
}))Untuk update yang bisa digabung, sebaiknya satu set saja dengan beberapa field. set({ status: 'loading', errors: {} }) lebih efisien daripada dua panggilan terpisah.
Zustand mendeteksi perubahan lewat perbandingan referensi. Prinsipnya:
// BURUK: mutasi langsung, tidak terdeteksi
set((s) => {
s.user.name = 'Baru'
return s
})Kode di atas mengembalikan object yang sama, sehingga komponen tidak re-render. Selalu buat object baru seperti pola spread di atas, atau gunakan middleware immer dari episode 9 untuk menulisnya dengan aman.
Episode 4 melengkapi senjata dasar kalian: create((set, get) => ...) membangun store, set memperbarui state dalam dua bentuk, get membaca state terkini, dan immutability menjaga deteksi perubahan tetap berfungsi.
Inti yang harus dibawa pulang:
Di episode 5 selanjutnya kita akan membahas selector dan subscription — memilih slice state agar re-render minimal, memakai useShallow untuk beberapa slice, dan subscribe manual dengan subscribeWithSelector untuk log, analitik, dan sinkronisasi non-React.