ViraStack
  • Academy
  • About
  • Support
HomeSourcesContribute
ViraStack

A frontend ecosystem: free guides, project templates, AI rules, and form components.

Products

  • ViraStack Start
  • ViraStack AI
  • ViraStack Mask
  • ViraStack Password
  • ViraStack Guide

Explore

  • Academy
  • About
  • Brand
  • Support

© 2026 ViraStack.

Open source · MIT

Zustand

07 / 09

Context ile store

Global store'un sınırları, createStore ile store'u Context'te paylaşmak, props'tan başlatmak, yeniden kullanılabilir component, test ve Next.js.

Global Store'un Sınırları

create ile kurulan store bir modül değişkenidir; uygulamanın her yerinden erişilir ve Provider gerekmez. TkDodo, bu kolaylığın üç durumda sorun çıkardığını anlatır:1

DurumSorun
Props'tan başlatmakStore component'ten önce oluşur. Değeri useEffect ile sonradan yazarsınız; ilk render yanlış değerle yapılır.
TestTüm testler aynı store'u paylaşır. Her testten sonra store'u elle sıfırlamanız gerekir.
Yeniden kullanımAynı component sayfada iki kez kullanılırsa iki örnek aynı state'i paylaşır ve birbirini ezer.

Next.js gibi sunucuda render eden bir framework'te dördüncü bir sorun var: sunucu aynı anda birçok isteğe cevap verir. Modül düzeyindeki bir store istekler arasında paylaşılır; bir kullanıcının verisi diğerine sızabilir.2

Değeri Değil, Store'u Paylaşın

Çözüm React Context'tir; ama state yönetimi için değil. Context'e değer yerine store örneğini koyarsınız. Store örneği hiç değişmediği için Context gereksiz render'a yol açmaz; değer seçimini yine Zustand'ın selector'ları yapar.1

Bunun için hook döndüren create yerine yalnızca store oluşturan createStore kullanılır:3

// src/features/filters/selection-store.ts
import { createStore } from "zustand";

export type SelectionState = {
  selected: string[];
  actions: {
    toggle: (id: string) => void;
    clear: () => void;
  };
};

export const createSelectionStore = (initialSelected: string[]) =>
  createStore<SelectionState>()((set) => ({
    selected: initialSelected,
    actions: {
      toggle: (id) =>
        set((state) => ({
          selected: state.selected.includes(id)
            ? state.selected.filter((s) => s !== id)
            : [...state.selected, id],
        })),
      clear: () => set({ selected: [] }),
    },
  }));

Provider store'u bir kez oluşturur. useState'in başlatıcı fonksiyonu yalnızca ilk render'da çalışır; TkDodo tek seferlik başlatma için useRef yerine bunu tercih eder:14

// src/features/filters/selection-provider.tsx
import { createContext, use, useState, type ReactNode } from "react";
import { useStore, type StoreApi } from "zustand";

const SelectionStoreContext = createContext<StoreApi<SelectionState> | null>(null);

export function SelectionProvider({
  initialSelected = [],
  children,
}: {
  initialSelected?: string[];
  children: ReactNode;
}) {
  const [store] = useState(() => createSelectionStore(initialSelected));
  return <SelectionStoreContext value={store}>{children}</SelectionStoreContext>;
}

function useSelectionStore<T>(selector: (state: SelectionState) => T): T {
  const store = use(SelectionStoreContext);
  if (!store) {
    throw new Error("useSelectionStore, SelectionProvider içinde kullanılmalı.");
  }
  return useStore(store, selector);
}

export const useIsSelected = (id: string) =>
  useSelectionStore((state) => state.selected.includes(id));
export const useSelectedCount = () => useSelectionStore((state) => state.selected.length);
export const useSelectionActions = () => useSelectionStore((state) => state.actions);

useStore(store, selector), create'in döndürdüğü hook'un yaptığını yapar: store'a selector ile abone olur.5 Provider dışında hata fırlatan hook kalıbı ve use Context dersinden tanıdık.

Üç Sorun Nasıl Çözülür

  • Props. initialSelected store oluşturulurken verilir; ilk render doğru değerle yapılır. Prop sonradan değişirse store'a yansımaz; bu bir başlangıç değeridir. Prop değişince store'u sıfırlamak istiyorsanız Provider'a key verin.1

  • Yeniden kullanım. Her Provider kendi store'unu oluşturur. Aynı filtre grubunu sayfada iki kez kullanabilirsiniz:

<SelectionProvider initialSelected={["shoes"]}>
  <CategoryFilter />
</SelectionProvider>
<SelectionProvider>
  <ColorFilter />
</SelectionProvider>
  • Test. Her test kendi Provider'ını render eder; store teste özeldir, sıfırlama gerekmez.1 Provider ile render eden yardımcı fonksiyon Provider ve hook testi dersinde.

Global store'larla devam ediyorsanız Zustand dokümanı testler için bir __mocks__/zustand.ts dosyasıyla her testten sonra tüm store'ları getInitialState()'e döndürmeyi anlatır.6 TkDodo bu yolu karmaşık bulduğunu yazar.1

Next.js'te Store

Zustand dokümanı App Router için iki kural koyar:2

  • Global store yok. Store istek başına oluşturulmalı; yukarıdaki Provider kalıbı bunu sağlar. Provider bir Client Component'tir.

  • Server Component store'u okumaz ve yazmaz. Server Component hook ve Context kullanamaz; store'a ulaşması mimariye aykırıdır.

// app/providers.tsx
"use client";

import { useState, type ReactNode } from "react";
import { CartStoreContext, createCartStore } from "@/features/cart/cart-store";

export function CartStoreProvider({ children }: { children: ReactNode }) {
  const [store] = useState(() => createCartStore());
  return <CartStoreContext value={store}>{children}</CartStoreContext>;
}

Provider'ı app/layout.tsx'te <body>'nin içine koyarsanız store uygulama boyunca yaşar. Yalnızca bir route'ta gerekiyorsa o route'un page.tsx'ine koyun; route'tan çıkınca store da gider.2 Server Component'te okunan bir değeri başlangıç değeri olarak Provider'a prop ile geçebilirsiniz.

Ne Zaman Hangisi

DurumÖneri
Uygulama genelinde tek bir state (tema, sepet), yalnızca tarayıcıdacreate ile global store
Bir route'a ya da component ağacına ait statecreateStore + Context
Props'tan başlaması gereken statecreateStore + Context
Sayfada birden fazla kez kullanılan componentcreateStore + Context
Sunucuda render edilen Next.js uygulamasıcreateStore + Context

TkDodo kendi deneyimini şöyle özetler: Context ile kurduğu store'ları, gerçekten global olanlardan daha sık kullanmıştır.1

Kendin Dene

  • Bir filtre grubunu global store ile yazın ve sayfada iki kez render edin. Birinde seçim yapınca diğerine ne oluyor?

  • Aynısını SelectionProvider ile yazın.

  • initialSelected prop'unu bir butonla değiştirin. Store güncelleniyor mu? Provider'a key verip tekrar deneyin.

  • useSelectedCount'u Provider'ın dışında çağırın ve hata mesajını görün.

Detaylı Öğrenmek İçin

  • Yazı: TkDodo, "Zustand and React Context"; global store'un üç sorunu ve Context ile çözümü.1

  • Resmi doküman: "Setup with Next.js"; istek başına store, Provider ve route düzeyinde store.2

  • Resmi doküman: "Initialize state with props"; createStore, Context ve custom hook kalıbı.3

Kaynaklar

  • 1ArticleTkDodo

    : — 14 Nisan 2024. Props'tan başlatma, test ve yeniden kullanım sorunları; store örneğini Context ile paylaşmak.

  • 2DocsZustand: Setup with Next.js

    İstek başına store, Server Component kuralı ve route düzeyinde Provider.

  • 3DocsZustand: Initialize state with props

    `createStore` ile store oluşturan fonksiyon, Context ve custom hook.

  • 4ArticleTkDodo

    : — 12 Mart 2021. Tek seferlik başlatma için `useState` başlatıcısı.

  • 5DocsZustand: useStore

    Vanilla store'a selector ile abone olmak.

  • 6DocsZustand: Testing

    Global store'ları testler arasında sıfırlayan mock.

Edit this page

Last updated: October 21, 2026

Previous step06. Persist ve devtoolsNext step08. TanStack Query ile birlikte kullanım