Zustand ile Modern State Yönetimi

Zustand ile Modern State Yönetimi
Zustand, React ekosistemindeki en hafif ve kullanımı en kolay state management kütüphanelerinden biridir. Sadece ~1.5 KB bundle boyutuyla, Redux'un boilerplate karmaşıklığından kaçınırken güçlü bir state yönetimi sunar. Bu rehberde, Zustand'ın temellerinden ileri düzey middleware kullanımına kadar kapsamlı bir yolculuk yapacağız.
Neden Zustand?
Zustand, Poimandres (pmndrs) topluluğu tarafından geliştirilmiş, minimalist bir state yönetim kütüphanesidir. Provider gerekmez, boilerplate kodu neredeyse sıfırdır ve selector bazlı yapısı sayesinde otomatik render optimizasyonu sağlar. Ayrıca React dışında (vanilla JS) da kullanılabilir.
Temel Kullanım
import { create } from 'zustand';
// Basit bir store
interface CounterStore {
count: number;
increment: () => void;
decrement: () => void;
incrementBy: (amount: number) => void;
reset: () => void;
}
const useCounterStore = create<CounterStore>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
incrementBy: (amount) => set((state) => ({ count: state.count + amount })),
reset: () => set({ count: 0 }),
}));
// Kullanım — Provider gerekmez!
function Counter() {
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
const decrement = useCounterStore((state) => state.decrement);
return (
<div>
<h2>Sayaç: {count}</h2>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
</div>
);
}
// Selector ile sadece ihtiyacınız olan state'i alın
// count değiştiğinde sadece bu component render olur
function CountDisplay() {
const count = useCounterStore((state) => state.count);
return <span>{count}</span>;
}
// Action'lar değişmez, gereksiz render olmaz
function CountButtons() {
const { increment, decrement, reset } = useCounterStore(
(state) => ({ increment: state.increment, decrement: state.decrement, reset: state.reset })
);
return (
<div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Sıfırla</button>
</div>
);
}
useCounterStore.getState().increment(). Bu özellik, API middleware'leri veya utility fonksiyonlarında çok işe yarar.
Gerçek Dünya Store: E-Ticaret Sepet
import { create } from 'zustand';
import { devtools, persist, subscribeWithSelector } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
image: string;
}
interface CartStore {
items: CartItem[];
isOpen: boolean;
// Actions
addItem: (item: Omit<CartItem, 'quantity'>) => void;
removeItem: (id: string) => void;
updateQuantity: (id: string, quantity: number) => void;
clearCart: () => void;
toggleCart: () => void;
// Computed
totalItems: () => number;
totalPrice: () => number;
}
const useCartStore = create<CartStore>()(
devtools(
persist(
subscribeWithSelector(
immer((set, get) => ({
items: [],
isOpen: false,
addItem: (item) =>
set((state) => {
const existing = state.items.find((i) => i.id === item.id);
if (existing) {
existing.quantity += 1;
} else {
state.items.push({ ...item, quantity: 1 });
}
}),
removeItem: (id) =>
set((state) => {
state.items = state.items.filter((i) => i.id !== id);
}),
updateQuantity: (id, quantity) =>
set((state) => {
const item = state.items.find((i) => i.id === id);
if (item) {
if (quantity <= 0) {
state.items = state.items.filter((i) => i.id !== id);
} else {
item.quantity = quantity;
}
}
}),
clearCart: () => set({ items: [] }),
toggleCart: () => set((state) => ({ isOpen: !state.isOpen })),
totalItems: () => get().items.reduce((sum, item) => sum + item.quantity, 0),
totalPrice: () =>
get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
}))
),
{
name: 'cart-storage',
partialize: (state) => ({ items: state.items }),
}
),
{ name: 'CartStore' }
)
);
// Sepet ikonunda kullanım
function CartIcon() {
const totalItems = useCartStore((state) => state.totalItems());
const toggleCart = useCartStore((state) => state.toggleCart);
return (
<button onClick={toggleCart} className="relative">
🛒
{totalItems > 0 && (
<span className="absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5 text-xs flex items-center justify-center">
{totalItems}
</span>
)}
</button>
);
}
Middleware Kullanımı
Zustand'ın middleware sistemi, store'unuza ekstra özellikler eklemenizi sağlar. En yaygın middleware'ler şunlardır:
| Middleware | Açıklama |
|---|---|
devtools |
Redux DevTools entegrasyonu |
persist |
State'i localStorage/sessionStorage'a kaydetme |
immer |
Mutable syntax ile immutable state güncellemesi |
subscribeWithSelector |
Store değişikliklerini selector bazlı dinleme |
Store Slices Pattern
import { create, StateCreator } from 'zustand';
// Her slice kendi state ve action'larını tanımlar
interface UserSlice {
user: { name: string; email: string } | null;
setUser: (user: UserSlice['user']) => void;
clearUser: () => void;
}
interface SettingsSlice {
theme: 'light' | 'dark';
language: 'tr' | 'en';
toggleTheme: () => void;
setLanguage: (lang: 'tr' | 'en') => void;
}
type CombinedStore = UserSlice & SettingsSlice;
const createUserSlice: StateCreator<CombinedStore, [], [], UserSlice> = (set) => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
});
const createSettingsSlice: StateCreator<CombinedStore, [], [], SettingsSlice> = (set) => ({
theme: 'light',
language: 'tr',
toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })),
setLanguage: (language) => set({ language }),
});
// Slice'ları birleştir
const useAppStore = create<CombinedStore>()((...args) => ({
...createUserSlice(...args),
...createSettingsSlice(...args),
}));
useStore((s) => ({ a: s.a, b: s.b })) her render'da yeni object oluşturur. Bunun yerine useShallow kullanın: useStore(useShallow((s) => ({ a: s.a, b: s.b }))).
useStore.setState(initialState) çağırın.
Sonuç
Zustand, minimal API'si ve güçlü middleware ekosistemiyle React state yönetiminde mükemmel bir seçenektir. Provider gerektirmemesi, selector bazlı optimizasyonu ve TypeScript desteğiyle hem küçük hem de büyük projelerde verimli çalışır. Immer ile mutable syntax, persist ile kalıcılık ve devtools ile debug kolaylığı sağlar. Eğer Redux'un karmaşıklığından bıktıysanız veya Context API'nin performans sınırlamalarıyla karşılaşıyorsanız, Zustand tam size göre.