02 / 09
İlk store
create ile store kurmak, TypeScript'te curried yazım, set ile birleştirme, get, async action ve store'a React dışından erişmek.
Kurulum
React rehberindeki gibi bir Vite projesi açın ve paketi ekleyin:1
$ pnpm add zustandPaket React 18 ve sonrasıyla çalışır. Ek bir Provider ya da kurulum dosyası gerekmez.2
create ile Store
Store'u create ile oluşturursunuz. Fonksiyon bir hook döndürür. İçine state'i ve onu değiştiren fonksiyonları (action) birlikte koyarsınız:13
// src/features/cart/cart-store.ts
import { create } from "zustand";
type CartItem = { id: number; name: string; price: number; quantity: number };
type CartState = {
items: CartItem[];
isOpen: boolean;
addItem: (item: Omit<CartItem, "quantity">) => void;
toggle: () => void;
clear: () => void;
};
export const useCartStore = create<CartState>()((set) => ({
items: [],
isOpen: false,
addItem: (item) =>
set((state) => ({ items: [...state.items, { ...item, quantity: 1 }] })),
toggle: () => set((state) => ({ isOpen: !state.isOpen })),
clear: () => set({ items: [] }),
}));TypeScript'te create(...) yerine create<CartState>()(...) yazılır. Fazladan () bilinçlidir: TypeScript, set ve get aynı tipi hem alıp hem verdiği için state tipini kendisi çıkaramaz. Curried yazım tipi elle vermenizi sağlarken middleware'lerin tiplerini çıkarmaya devam eder.4
Dosya adı ve yeri için genel kural: store'u kullanan özelliğin klasörüne koyun (bkz. Component ve Kod Organizasyonu).
Component'te Kullanmak
Hook'a bir selector verirsiniz. Component yalnızca selector'ın döndürdüğü değer değiştiğinde render olur:1
function CartBadge() {
const count = useCartStore((state) => state.items.length);
return <span>{count}</span>;
}
function AddButton() {
const addItem = useCartStore((state) => state.addItem);
return (
<button onClick={() => addItem({ id: 1, name: "Canvas Sneaker", price: 1200 })}>
Sepete ekle
</button>
);
}AddButton yalnızca addItem'ı okur. Action'lar hiç değişmediği için sepete ürün eklendiğinde bu buton render olmaz. Selector'ların ayrıntısı Selector ve yeniden render dersinde.
set: Birleştirerek Günceller
set'e verdiğiniz nesne mevcut state ile birleştirilir; yalnızca verdiğiniz alanlar değişir. Önceki state'e göre güncellerken fonksiyon verin.35
set({ items: [] }); // isOpen ve action'lar olduğu gibi kalır
set((state) => ({ isOpen: !state.isOpen })); // önceki değere göreBirleştirme yalnızca bir seviye derindir. İç içe bir nesneyi güncellerken onu kendiniz kopyalamalısınız:5
type ProfileState = {
user: { name: string; theme: "light" | "dark" };
setTheme: (theme: "light" | "dark") => void;
};
const useProfileStore = create<ProfileState>()((set) => ({
user: { name: "Ada", theme: "light" },
// ❌ set({ user: { theme } }) name alanını siler
setTheme: (theme) => set((state) => ({ user: { ...state.user, theme } })),
}));State'i değiştirmeyin, yeni değer döndürün. state.items.push(item) yazmak store'u değiştirir ama aboneler haberdar olmaz; ekran güncellenmez.5 React'teki kural aynıdır (bkz. State).
set'in ikinci argümanı true olursa state birleştirilmez, değiştirilir. v5'te bu durumda TypeScript tam bir state ister; eksik alan verirseniz derlenmez.2 Action'ları da sileceği için nadiren gerekir.
get ve Async Action
create'e verdiğiniz fonksiyonun ikinci parametresi get'tir; action içinde güncel state'i okur. Action'lar async olabilir; Zustand için fark etmez, iş bitince set çağırırsınız:4
type CouponState = {
code: string | null;
discount: number;
applyCoupon: (code: string) => Promise<void>;
};
const useCouponStore = create<CouponState>()((set, get) => ({
code: null,
discount: 0,
applyCoupon: async (code) => {
if (get().code === code) return;
const { discount } = await validateCoupon(code);
set({ code, discount });
},
}));Warning
Sunucudan veri çekip store'a yazmak bir kez için kolaydır. Yükleme durumu, hata, yeniden deneme ve cache gerektiğinde bu işi TanStack Query'ye bırakın. Birlikte kullanım TanStack Query ile birlikte kullanım dersinde.
React Dışından Erişmek
Hook aynı zamanda store'un kendisidir. Üzerindeki fonksiyonlarla state'i React dışından okuyup değiştirebilirsiniz:6
| Fonksiyon | Ne yapar |
|---|---|
getState() | Güncel state'i döndürür; abone olmaz |
setState(partial) | set ile aynı; birleştirerek günceller |
subscribe(listener) | Her değişiklikte (state, prevState) ile çağrılır; aboneliği bitiren fonksiyon döndürür |
getInitialState() | Store oluşturulurken verilen ilk state |
// Bir WebSocket mesajında
socket.on("cart-cleared", () => useCartStore.setState({ items: [] }));
const unsubscribe = useCartStore.subscribe((state, prevState) => {
if (state.items.length !== prevState.items.length) {
analytics.track("cart_changed", { count: state.items.length });
}
});Component içinde useCartStore.getState() ile değer okumayın; abone olmadığınız için değer değişince component güncellenmez. Event handler içinde güncel değeri okumak için kullanabilirsiniz.
Tip Yazmadan: combine
State tipini ayrıca yazmak istemiyorsanız combine middleware'i ilk state'ten tipi çıkarır. Bu durumda curried yazım gerekmez:4
import { create, type ExtractState } from "zustand";
import { combine } from "zustand/middleware";
export const useCounterStore = create(
combine({ count: 0 }, (set) => ({
increase: () => set((state) => ({ count: state.count + 1 })),
})),
);
export type CounterState = ExtractState<typeof useCounterStore>;Bedeli küçük bir tip açığıdır: action'ların içindeki get ve set yalnızca ilk state'i bilir, action'ları görmez. Dokümanın notu: çoğu durumda sorun çıkarmaz, set(..., true) ile değiştirirken dikkat edin.4 ExtractState, store'un tam tipini testlerde ve yardımcı fonksiyonlarda kullanmak için verilir.7
Kendin Dene
Sepet store'unu kurun.
CartBadgeveAddButton'aconsole.log("render")koyun; ürün eklerken hangisi loglanıyor?addItem'ıstate.items.push(...)ile yazın. Ekran güncelleniyor mu?getState().itemsne diyor?Tarayıcı konsolunda store'u
window'a bağlayıpsetState({ isOpen: true })çağırın; ekran güncelleniyor mu?set({ isOpen: true }, true)yazın. TypeScript ne diyor?
Detaylı Öğrenmek İçin
Resmi doküman: "Beginner TypeScript Guide"; tipli store,
ExtractState, sıfırlama ve birden fazla store.7Resmi doküman: "Advanced TypeScript Guide"; curried yazımın ve tip çıkarımının nedeni.4
Resmi doküman: "Updating state"; düz ve iç içe güncellemeler.5
Kaynaklar
Kurulum, hook olarak store ve selector ile abonelik.
React 18 şartı ve `replace` bayrağı için sıkılaşan tipler.
`create` imzası, `set` ve `get` ile state ve action tanımlamak.
Curried `create<T>()`, `combine` ile tip çıkarımı ve sınırları.
`set`'in birleştirmesi ve iç içe nesneleri kopyalamak.
`getState`, `setState`, `subscribe` ve `getInitialState`.
Tipli store, `ExtractState`, async action ve birden fazla store.
Last updated: October 21, 2026
