Next.js ile Zustand Kullanımı: Modern ve Hafif Durum Yönetimi

Zustand hafif, esnek ve performansı yüksek bir durum yönetim kütüphanesidir.

Bu makalemde, Next.js App Router ile Zustand’ı nasıl entegre edeceğinizi, durum yönetimini nasıl optimize edeceğinizi ve gerçek senaryolara uygun örneklerle nasıl kullanacağınızı detaylı bir şekilde ele alacağım.

Zustand Nedir?

Zustand, Redux gibi karmaşık kütüphanelere alternatif olarak geliştirilmiş, Redux'a göre daha minimal ve kullanımı kolay bir durum yönetim kütüphanesidir. React uygulamaları için tasarlanmıştır ve aşağıdaki özellikleriyle öne çıkar:

  • Hafif ve Minimal: Zustand, yalnızca birkaç KB boyutundadır ve boilerplate kodu en aza indirir.
  • Esnek API: Redux’taki gibi action ve reducer tanımlamaya gerek kalmadan doğrudan durum güncellemeleri yapabilirsiniz.
  • Performans Odaklı: Zustand, yalnızca ilgili bileşenlerin yeniden render olmasını sağlar, bu da performansı artırır.
  • TypeScript Desteği: Güçlü TypeScript entegrasyonu ile tip güvenliği sağlar.

Zustand, özellikle Next.js App Router ile birlikte kullanıldığında, istemci ve sunucu bileşenleri arasında durumu yönetmek için ideal bir seçimdir.

Next.js App Router ve Zustand Entegrasyonu

Next.js App Router, sunucu bileşenlerini varsayılan olarak kullanır ve istemci bileşenlerini yalnızca gerektiğinde devreye sokar. Zustand, yalnızca istemci tarafında çalıştığı için "use client" direktifiyle işaretlenmiş bileşenlerde kullanılmalıdır. Aşağıda, Next.js App Router ile Zustand’ı entegre etme adımlarını detaylı bir şekilde açıklayacağım.

1. Proje Kurulumu

Öncelikle, yeni bir Next.js projesi oluşturun ve gerekli bağımlılıkları yükleyin:


    npx create-next-app@latest my-zustand-app
    cd my-zustand-app
    npm install zustand

2. Zustand Store Oluşturma

Zustand’da durum yönetimi, bir "store" oluşturarak başlar. Store, uygulamanızın global durumunu tutar ve bileşenler arasında paylaşılır. Örnek bir store oluşturmak için app/store.ts dosyasını oluşturun:


    // app/store.ts
    import { create } from 'zustand';

    interface CounterState {
      count: number;
      increment: () => void;
      decrement: () => void;
      reset: () => void;
    }

    export const useCounterStore = create<CounterState>((set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
      decrement: () => set((state) => ({ count: state.count - 1 })),
      reset: () => set({ count: 0 }),
    }));

Bu store, bir sayaç örneği içeriyor. count değeri tutuluyor ve increment, decrement ile reset fonksiyonları durumu güncelliyor.

3. İstemci Bileşeninde Zustand Kullanımı

Next.js App Router’da Zustand’ı kullanmak için bir istemci bileşeni oluşturmanız gerekir. Çünkü Zustand, istemci tarafında çalışan bir kütüphanedir ve sunucu bileşenlerinde doğrudan kullanılamaz. app/components/Counter.tsx adında bir istemci bileşeni oluşturalım:


    // app/components/Counter.tsx
    'use client';

    import { useCounterStore } from '../store';

    export default function Counter() {
      const { count, increment, decrement, reset } = useCounterStore();

      return (
        <div className="p-4 border rounded shadow">
          <h2 className="text-xl font-bold">Sayaç: {count}</h2>
          <div className="mt-4 space-x-2">
            <button
              onClick={increment}
              className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
            >
              Artır
            </button>
            <button
              onClick={decrement}
              className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
            >
              Azalt
            </button>
            <button
              onClick={reset}
              className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600"
            >
              Sıfırla
            </button>
          </div>
        </div>
      );
    }

Bu bileşen, Zustand store’undan count değerini ve ilgili fonksiyonları alarak bir sayaç arayüzü oluşturuyor. "use client" direktifi, bu bileşenin istemci tarafında çalışacağını belirtir.

4. Sunucu Bileşeninde İstemci Bileşenini Kullanma

Next.js App Router’da, istemci bileşenlerini sunucu bileşenleri içinde kullanabilirsiniz. app/page.tsx dosyasını düzenleyerek Counter bileşenini ekleyelim:


    // app/page.tsx
    import Counter from './components/Counter';

    export default function Home() {
      return (
        <div className="min-h-screen flex items-center justify-center bg-gray-100">
          <Counter />
        </div>
      );
    }

Bu sayfa, bir sunucu bileşeni olarak çalışır ve istemci bileşeni olan Counter’ı içinde render eder.

5. Zustand ile Gelişmiş Durum Yönetimi

Zustand, yalnızca basit durumlar için değil, karmaşık senaryolar için de kullanılabilir. Örneğin, bir kullanıcı yönetim sistemi için store oluşturabilirsiniz:


    // app/store.ts
    import { create } from 'zustand';
    import { persist } from 'zustand/middleware';

    interface UserState {
      user: { name: string; email: string } | null;
      login: (name: string, email: string) => void;
      logout: () => void;
    }

    export const useUserStore = create<UserState>()(
      persist(
        (set) => ({
          user: null,
          login: (name, email) => set({ user: { name, email } }),
          logout: () => set({ user: null }),
        }),
        {
          name: 'user-storage', // Tarayıcıda saklanacak anahtar
          storage: createJSONStorage(() => localStorage), // localStorage kullanımı
        }
      )
    );

Bu store, kullanıcı bilgilerini tutar ve persist middleware’i ile durumu tarayıcının localStorage’ında saklar. Böylece sayfa yenilendiğinde kullanıcı bilgileri kaybolmaz.

6. Zustand ile Performans Optimizasyonu

Zustand, performansı artırmak için birkaç güçlü özellik sunar:

  • Seçici Kullanım: Zustand’da yalnızca ihtiyacınız olan durum parçalarını seçebilirsiniz. Bu, gereksiz yeniden render işlemlerini önler:

    const count = useCounterStore((state) => state.count);

  • Middleware Kullanımı: persist, immer gibi middleware’ler ile durumu daha kolay yönetebilirsiniz. Örneğin, immer ile karmaşık durum güncellemeleri basitleşir:

    import { create } from 'zustand';
    import { immer } from 'zustand/middleware/immer';

    interface ComplexState {
      data: { items: string[] };
      addItem: (item: string) => void;
    }

    export const useComplexStore = create<ComplexState>()(
      immer((set) => ({
        data: { items: [] },
        addItem: (item) =>
          set((state) => {
            state.data.items.push(item);
          }),
      }))
    );

  • Sunucu/İstemci Ayrımı: Zustand’ı yalnızca istemci bileşenlerinde kullanmak, Next.js’in sunucu bileşenlerinin performans avantajlarını korur.
7. Gerçek Dünya Örneği: Sepet Uygulaması

Bir e-ticaret uygulamasında sepet yönetimini örnekleyelim:


    // app/store.ts
    import { create } from 'zustand';

    interface CartItem {
      id: string;
      name: string;
      price: number;
      quantity: number;
    }

    interface CartState {
      cart: CartItem[];
      addToCart: (item: CartItem) => void;
      removeFromCart: (id: string) => void;
      clearCart: () => void;
    }

    export const useCartStore = create<CartState>((set) => ({
      cart: [],
      addToCart: (item) =>
        set((state) => ({
          cart: state.cart.some((i) => i.id === item.id)
            ? state.cart.map((i) =>
                i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
              )
            : [...state.cart, { ...item, quantity: 1 }],
        })),
      removeFromCart: (id) =>
        set((state) => ({ cart: state.cart.filter((item) => item.id !== id) })),
      clearCart: () => set({ cart: [] }),
    }));

Bu store’u kullanarak bir sepet bileşeni oluşturabilirsiniz:


    // app/components/Cart.tsx
    'use client';

    import { useCartStore } from '../store';

    export default function Cart() {
      const { cart, addToCart, removeFromCart, clearCart } = useCartStore();

      return (
        <div className="p-4 border rounded shadow">
          <h2 className="text-xl font-bold">Sepet</h2>
          {cart.length === 0 ? (
            <p>Sepet boş</p>
          ) : (
            <ul>
              {cart.map((item) => (
                <li key={item.id} className="flex justify-between py-2">
                  <span>
                    {item.name} (x{item.quantity}) - ${item.price}
                  </span>
                  <button
                    onClick={() => removeFromCart(item.id)}
                    className="text-red-500"
                  >
                    Kaldır
                  </button>
                </li>
              ))}
            </ul>
          )}
          <button
            onClick={() =>
              addToCart({
                id: '1',
                name: 'Ürün 1',
                price: 29.99,
                quantity: 1,
              })
            }
            className="mt-4 px-4 py-2 bg-green-500 text-white rounded"
          >
            Ürün Ekle
          </button>
          <button
            onClick={clearCart}
            className="mt-4 ml-2 px-4 py-2 bg-red-500 text-white rounded"
          >
            Sepeti Temizle
          </button>
        </div>
      );
    }

8. En İyi Uygulamalar
  • Store’ları Modüler Tutun: Her store’u belirli bir amaç için oluşturun (örneğin, kullanıcı, sepet, tema).
  • TypeScript Kullanın: Tip güvenliği için TypeScript ile store’larınızı tanımlayın.
  • Sunucu ve İstemci Ayrımına Dikkat Edin: Zustand’ı yalnızca "use client" ile işaretlenmiş bileşenlerde kullanın.
  • Persist ile Kalıcı Durum: Kullanıcı oturumları gibi kalıcı veriler için persist middleware’ini kullanın.
  • Test Edilebilirlik: Zustand store’larını test etmek için jest veya vitest ile kolayca mock yapabilirsiniz.
Sonuç

Next.js App Router ile Zustand, modern web uygulamaları için güçlü ve hafif bir durum yönetimi çözümü sunar. Zustand’ın minimal yapısı, Next.js’in sunucu ve istemci bileşen mimarisiyle mükemmel bir uyum sağlar.

Bu makalemde, temel bir sayaç örneğinden başlayarak, kullanıcı yönetimi ve sepet uygulaması gibi gerçek dünya senaryolarına kadar Zustand’ın nasıl kullanılacağını detaylı bir şekilde inceledim. Bu rehberi takip ederek, siz de Next.js projelerinizde Zustand ile verimli ve performans odaklı durum yönetimi yapabilirsiniz.


08 Ara 2025
Yorum