İçeriğe geç
react

React State Management: Redux vs Zustand vs Jotai vs Context

Tarık Tunç
React State Management: Redux vs Zustand vs Jotai vs Context

React State Management: Redux vs Zustand vs Jotai vs Context

React uygulamalarında state yönetimi, projenin büyüklüğüne ve karmaşıklığına göre farklı yaklaşımlar gerektirir. Bu rehberde, en popüler dört state management çözümünü — Redux Toolkit, Zustand, Jotai ve Context API — detaylı şekilde karşılaştıracağız. Her birinin güçlü ve zayıf yönlerini, kullanım senaryolarını ve performans özelliklerini inceleyeceğiz.

1. Context API: React'in Yerleşik Çözümü

Context API, React'in kendi içinde sunduğu en basit state paylaşım mekanizmasıdır. Küçük ve orta ölçekli uygulamalarda harici bir kütüphane kullanmadan state paylaşımı yapmanızı sağlar. Ancak performans açısından dikkatli kullanılması gereken önemli noktalar vardır.

import React, { createContext, useContext, useState } from 'react';

interface AuthContextType {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
  isLoading: boolean;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  const login = async (email: string, password: string) => {
    setIsLoading(true);
    try {
      const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      });
      const userData = await response.json();
      setUser(userData);
    } finally {
      setIsLoading(false);
    }
  };

  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout, isLoading }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be used within AuthProvider');
  return context;
}
Uyarı: Context API'de her value değişikliğinde, o context'i consume eden tüm componentler yeniden render olur. Bu nedenle sık değişen veriler için Context API yerine Zustand veya Jotai tercih edilmelidir.

2. Redux Toolkit: Endüstri Standardı

Redux Toolkit (RTK), Redux'un modern ve önerilen kullanım şeklidir. createSlice, createAsyncThunk gibi yardımcı fonksiyonlarla boilerplate kodunu minimize eder. Büyük ekiplerde ve karmaşık uygulamalarda güçlü DevTools desteğiyle öne çıkar.

import { createSlice, createAsyncThunk, configureStore } from '@reduxjs/toolkit';

interface Product {
  id: string;
  name: string;
  price: number;
}

interface CartState {
  items: Product[];
  total: number;
  status: 'idle' | 'loading' | 'succeeded' | 'failed';
}

const initialState: CartState = {
  items: [],
  total: 0,
  status: 'idle',
};

export const fetchCart = createAsyncThunk('cart/fetch', async (userId: string) => {
  const response = await fetch(`/api/cart/${userId}`);
  return response.json();
});

const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {
    addItem(state, action) {
      state.items.push(action.payload);
      state.total += action.payload.price;
    },
    removeItem(state, action) {
      const index = state.items.findIndex(item => item.id === action.payload);
      if (index !== -1) {
        state.total -= state.items[index].price;
        state.items.splice(index, 1);
      }
    },
    clearCart(state) {
      state.items = [];
      state.total = 0;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchCart.pending, (state) => { state.status = 'loading'; })
      .addCase(fetchCart.fulfilled, (state, action) => {
        state.status = 'succeeded';
        state.items = action.payload.items;
        state.total = action.payload.total;
      })
      .addCase(fetchCart.rejected, (state) => { state.status = 'failed'; });
  },
});

export const { addItem, removeItem, clearCart } = cartSlice.actions;

const store = configureStore({
  reducer: { cart: cartSlice.reducer },
});

export type RootState = ReturnType<typeof store.getState>;
export default store;

3. Zustand: Minimalist ve Güçlü

Zustand, Jotai'nin yaratıcısı Daishi Kato tarafından geliştirilmiş, son derece hafif ve performanslı bir state management kütüphanesidir. Boilerplate kodu neredeyse sıfırdır ve React dışında da kullanılabilir. Selector bazlı yapısı sayesinde gereksiz render'ları otomatik olarak önler.

import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

interface TodoStore {
  todos: Todo[];
  filter: 'all' | 'active' | 'completed';
  addTodo: (text: string) => void;
  toggleTodo: (id: string) => void;
  removeTodo: (id: string) => void;
  setFilter: (filter: 'all' | 'active' | 'completed') => void;
  filteredTodos: () => Todo[];
}

const useTodoStore = create<TodoStore>()(
  devtools(
    persist(
      (set, get) => ({
        todos: [],
        filter: 'all',
        addTodo: (text) =>
          set((state) => ({
            todos: [...state.todos, { id: crypto.randomUUID(), text, completed: false }],
          })),
        toggleTodo: (id) =>
          set((state) => ({
            todos: state.todos.map((todo) =>
              todo.id === id ? { ...todo, completed: !todo.completed } : todo
            ),
          })),
        removeTodo: (id) =>
          set((state) => ({
            todos: state.todos.filter((todo) => todo.id !== id),
          })),
        setFilter: (filter) => set({ filter }),
        filteredTodos: () => {
          const { todos, filter } = get();
          if (filter === 'active') return todos.filter((t) => !t.completed);
          if (filter === 'completed') return todos.filter((t) => t.completed);
          return todos;
        },
      }),
      { name: 'todo-storage' }
    )
  )
);

4. Jotai: Atomic State Management

Jotai, atom bazlı bir state management yaklaşımı sunar. Her atom bağımsız bir state parçasını temsil eder ve sadece o atomu kullanan componentler yeniden render olur. Bu yaklaşım özellikle büyük ve karmaşık state ağaçlarında mükemmel performans sağlar. Recoil'den ilham almış ancak çok daha minimal bir API sunar.

import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';

// Temel atomlar
const darkModeAtom = atomWithStorage('darkMode', false);
const fontSizeAtom = atom(16);
const languageAtom = atom<'tr' | 'en'>('tr');

// Derived (türetilmiş) atom
const themeAtom = atom((get) => ({
  isDark: get(darkModeAtom),
  fontSize: get(fontSizeAtom),
  language: get(languageAtom),
  cssVariables: {
    '--bg-color': get(darkModeAtom) ? '#1a1a2e' : '#ffffff',
    '--text-color': get(darkModeAtom) ? '#e0e0e0' : '#333333',
    '--font-size': `${get(fontSizeAtom)}px`,
  },
}));

// Async atom
const userProfileAtom = atom(async () => {
  const response = await fetch('/api/profile');
  return response.json();
});

// Write-only atom
const resetSettingsAtom = atom(null, (get, set) => {
  set(darkModeAtom, false);
  set(fontSizeAtom, 16);
  set(languageAtom, 'tr');
});

function SettingsPanel() {
  const [darkMode, setDarkMode] = useAtom(darkModeAtom);
  const theme = useAtomValue(themeAtom);
  const resetSettings = useSetAtom(resetSettingsAtom);

  return (
    <div style={theme.cssVariables}>
      <label>
        <input type="checkbox" checked={darkMode} onChange={(e) => setDarkMode(e.target.checked)} />
        Karanlık Mod
      </label>
      <button onClick={resetSettings}>Ayarları Sıfırla</button>
    </div>
  );
}

Karşılaştırma Tablosu

Özellik Context API Redux Toolkit Zustand Jotai
Bundle Boyutu 0 KB ~11 KB ~1.5 KB ~3 KB
Öğrenme Eğrisi Düşük Orta-Yüksek Düşük Düşük
DevTools React DevTools Redux DevTools Redux DevTools Jotai DevTools
Performans Zayıf (büyük state) İyi Çok İyi Çok İyi
TypeScript Desteği İyi Mükemmel Mükemmel Mükemmel
Middleware Yok Zengin Var Sınırlı
SSR Desteği Var Var Var Var
İpucu: Proje seçimi yaparken şu kuralı izleyin: Küçük projeler için Context API, orta ölçekli projeler için Zustand veya Jotai, büyük enterprise projeler için Redux Toolkit idealdir.

Hangi Durumda Hangisi?

  • Context API: Tema, dil ayarları gibi nadiren değişen global state'ler için idealdir.
  • Redux Toolkit: Karmaşık iş mantığı, middleware ihtiyacı ve büyük ekiplerde standartlaşma gereken projelerde tercih edilmelidir.
  • Zustand: Hızlı prototipleme, orta ölçekli projeler ve minimal boilerplate isteyenler için mükemmeldir.
  • Jotai: Bağımsız, granüler state parçaları olan uygulamalarda ve form-heavy projelerde öne çıkar.

Sonuç

React ekosisteminde "en iyi" state management çözümü diye bir şey yoktur — projenizin ihtiyaçlarına en uygun olanı seçmelisiniz. Context API basitliğiyle, Redux Toolkit olgunluğuyla, Zustand minimalizmiyle ve Jotai atom bazlı yaklaşımıyla farklı senaryolarda parlar. Önemli olan, seçtiğiniz çözümü tutarlı şekilde kullanmak ve gereksiz karmaşıklıktan kaçınmaktır. Küçük başlayın, gerektiğinde ölçeklendirin.

Kaynaklar

İlgili Yazılar