Zustand Neden Redux'u Geçti? 57K Yıldızlı Minimal State Yönetimi Devrimi
React ekosisteminde state yönetimi her zaman en çok tartışılan konulardan biri olmuştur. Yıllarca Redux bu alanın tartışılmaz kralıydı. Ancak 2024-2025 itibarıyla tabloda ciddi bir değişim yaşanıyor: Zustand, npm indirme sayıları ve geliştirici memnuniyetinde Redux'u geride bırakmaya başladı. Peki 3KB'lık bu minik kütüphane, devasa bir ekosisteme sahip Redux'u nasıl tahtından etti?
Bu yazıda Zustand'ın yükselişini, Redux ile karşılaştırmasını ve neden modern React projelerinde ilk tercih haline geldiğini detaylıca inceleyeceğiz.
State Yönetimi Sorunu: Nereden Geldik?
React'in kendi useState ve useContext hook'ları küçük uygulamalar için yeterlidir. Ancak uygulama büyüdükçe şu sorunlarla karşılaşırız:
- Prop drilling: State'in derinlerdeki bileşenlere iletilmesi
- Context performans sorunları: Context değiştiğinde tüm consumer'ların yeniden render edilmesi
- Karmaşık state mantığı: Birbirine bağlı state'lerin yönetilmesi
- Paylaşılan state: Farklı bileşen ağaçlarının aynı veriye erişmesi
Redux bu sorunları çözmek için 2015'te sahneye çıktı ve yıllarca standart oldu. Ancak beraberinde kendi sorunlarını da getirdi.
Redux'un Sorunlu Mirası
Redux'u bir projede kullanmak istediğinizde minimum düzeyde şunlara ihtiyacınız var:
// 1. Action Types
const INCREMENT = 'counter/INCREMENT';
const DECREMENT = 'counter/DECREMENT';
// 2. Action Creators
const increment = () => ({ type: INCREMENT });
const decrement = () => ({ type: DECREMENT });
// 3. Reducer
const counterReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 };
case DECREMENT:
return { ...state, count: state.count - 1 };
default:
return state;
}
};
// 4. Store
import { createStore } from 'redux';
const store = createStore(counterReducer);
// 5. Provider ile sarmalama
import { Provider } from 'react-redux';
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
// 6. Bileşende kullanım
import { useSelector, useDispatch } from 'react-redux';
function Counter() {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<div>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}Basit bir sayaç için 6 farklı kavram, en az 3 ayrı dosya ve onlarca satır kod. Redux Toolkit bu durumu iyileştirdi, ancak temel karmaşıklık hâlâ mevcuttur:
// Redux Toolkit ile
import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => { state.count += 1; },
decrement: (state) => { state.count -= 1; },
},
});
export const { increment, decrement } = counterSlice.actions;
const store = configureStore({ reducer: counterSlice.reducer });Daha iyi, ama hâlâ slice, action, reducer, store, Provider ve selector kavramlarını bilmeniz gerekiyor.
Zustand: "Ayı" Gibi Güçlü, "Tüy" Gibi Hafif
Zustand, Almanca'da "durum" (state) anlamına gelir. Jotai ve Valtio'nun da yaratıcısı olan Daishi Kato ve Poimandres ekibi tarafından geliştirilmiştir.
Aynı sayaç örneğini Zustand ile yazalım:
import { create } from 'zustand';
// Store oluştur — hepsi bu
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
// Bileşende kullan — Provider yok, dispatch yok
function Counter() {
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
const decrement = useCounterStore((state) => state.decrement);
return (
<div>
<span>{count}</span>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}Hiç Provider yok. Hiç action type yok. Hiç reducer yok. Hiç dispatch yok.
Tek bir create fonksiyonu çağrısıyla store oluşturuyorsunuz ve dönen hook'u doğrudan bileşenlerinizde kullanıyorsunuz. İşte bu kadar.
Zustand'ın Redux'u Geçmesinin 7 Temel Nedeni
1. Sıfır Boilerplate
Yukarıdaki örnekte gördüğünüz gibi, Zustand ile bir store oluşturmak tek bir fonksiyon çağrısıdır. Redux'un action → dispatch → reducer → store döngüsü tamamen ortadan kalkar.
2. Provider Gerektirmez
Redux'un <Provider> bileşeni ile uygulamanızı sarmanız gerekir. Zustand store'ları modül seviyesinde yaşar ve React ağacından bağımsızdır:
// store.js - React'ten tamamen bağımsız
const useStore = create((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
// Herhangi bir bileşende, herhangi bir yerde import et ve kullan
import { useStore } from './store';Bu özellik özellikle micro-frontend mimarilerinde ve test yazarken büyük avantaj sağlar.
3. Otomatik Render Optimizasyonu
Zustand, selector pattern ile sadece değişen state parçasını izler. Gereksiz render'ları otomatik olarak engeller:
// ✅ Sadece "count" değiştiğinde render olur
const count = useCounterStore((state) => state.count);
// ✅ Sadece "user.name" değiştiğinde render olur
const userName = useStore((state) => state.user.name);
// ⚠️ Shallow equality ile birden fazla değer seçme
import { useShallow } from 'zustand/react/shallow';
const { count, increment } = useCounterStore(
useShallow((state) => ({
count: state.count,
increment: state.increment,
}))
);4. Middleware Desteği — Tanıdık Ama Daha Basit
Zustand, Redux'un middleware konseptini çok daha zarif bir şekilde sunar:
import { create } from 'zustand';
import { devtools, persist, subscribeWithSelector } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
const useStore = create(
devtools(
persist(
immer((set) => ({
todos: [],
addTodo: (todo) =>
set((state) => {
state.todos.push(todo); // Immer ile doğrudan mutasyon!
}),
removeTodo: (id) =>
set((state) => {
state.todos = state.todos.filter((t) => t.id !== id);
}),
})),
{ name: 'todo-storage' } // localStorage'a otomatik kayıt
),
{ name: 'TodoStore' } // Redux DevTools'da görünecek isim
)
);Bu tek store tanımıyla şunları elde ediyorsunuz:
- Redux DevTools entegrasyonu (
devtools) - localStorage persistence (
persist) - Immer ile kolay immutable güncellemeler (
immer)
5. Async İşlemler Doğal Olarak Desteklenir
Redux'ta async işlemler için redux-thunk veya redux-saga gibi ek kütüphanelere ihtiyaç vardır. Zustand'da async fonksiyonlar doğal olarak çalışır:
const useProductStore = create((set, get) => ({
products: [],
loading: false,
error: null,
fetchProducts: async () => {
set({ loading: true, error: null });
try {
const response = await fetch('/api/products');
const products = await response.json();
set({ products, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
getProductById: (id) => {
// get() ile mevcut state'e erişim
return get().products.find((p) => p.id === id);
},
}));
// Bileşende kullanım
function ProductList() {
const { products, loading, fetchProducts } = useProductStore(
useShallow((s) => ({
products: s.products,
loading: s.loading,
fetchProducts: s.fetchProducts,
}))
);
useEffect(() => {
fetchProducts();
}, [fetchProducts]);
if (loading) return <Spinner />;
return products.map((p) => <ProductCard key={p.id} product={p} />);
}Hiçbir middleware, thunk veya saga gerektirmeden temiz async state yönetimi.
6. React Dışında da Kullanılabilir
Zustand store'ları React hook'u olarak kullanılabildiği gibi, vanilla JavaScript'te de çalışır:
const useStore = create((set) => ({
theme: 'light',
toggleTheme: () =>
set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light',
})),
}));
// React dışında — örneğin bir utility fonksiyonunda
const currentTheme = useStore.getState().theme;
// State değişikliklerini dinle
const unsubscribe = useStore.subscribe((state) => {
document.body.className = state.theme;
});
// State'i dışarıdan güncelle
useStore.getState().toggleTheme();Bu özellik, framework-agnostic kütüphaneler yazarken veya React dışı entegrasyonlarda (analytics, web workers vb.) çok değerlidir.
7. TypeScript ile Mükemmel Uyum
Zustand'ın TypeScript desteği birinci sınıftır ve minimum tip tanımı gerektirir:
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const useAuthStore = create<AuthState>()((set) => ({
user: null,
token: null,
isAuthenticated: false,
login: async (email, password) => {
const { user, token } = await authApi.login(email, password);
set({ user, token, isAuthenticated: true });
},
logout: () => {
set({ user: null, token: null, isAuthenticated: false });
},
}));
// Tam tip güvenliği ile kullanım
const user = useAuthStore((s) => s.user); // User | null — otomatik tip çıkarımıStore Tasarım Desenleri: Gerçek Dünya Örnekleri
Slice Pattern — Büyük Uygulamalar İçin
Büyük uygulamalarda store'u slice'lara ayırabilirsiniz:
// slices/authSlice.ts
export const createAuthSlice = (set, get) => ({
user: null,
login: async (credentials) => {
const user = await api.login(credentials);
set({ user });
},
logout: () => set({ user: null }),
});
// slices/cartSlice.ts
export const createCartSlice = (set, get) => ({
items: [],
addItem: (item) =>
set((state) => ({ items: [...state.items, item] })),
getTotalPrice: () =>
get().items.reduce((sum, item) => sum + item.price, 0),
});
// store.ts — slice'ları birleştir
const useBoundStore = create((...args) => ({
...createAuthSlice(...args),
...createCartSlice(...args),
}));Computed Values (Türetilmiş Değerler)
const useStore = create((set, get) => ({
items: [],
filter: 'all',
// Türetilmiş değer getter ile
getFilteredItems: () => {
const { items, filter } = get();
if (filter === 'all') return items;
return items.filter((item) => item.status === filter);
},
getItemCount: () => get().items.length,
}));Sayılarla Zustand vs Redux (2025)
| Metrik | Zustand | Redux (Core) | Redux Toolkit |
|---|---|---|---|
| GitHub Yıldızı | ~57K | ~61K | ~10K |
| Bundle Boyutu | ~3KB (gzip) | ~7KB (gzip) | ~30KB (gzip) |
| Haftalık npm İndirme | ~13M | ~10M | ~11M |
| Boilerplate | Minimal | Yüksek | Orta |
| Öğrenme Eğrisi | Düşük | Yüksek | Orta |
| Provider Gereksinimi | Hayır | Evet | Evet |
| Built-in Async | Evet | Hayır | Kısmen |
| DevTools | Middleware ile | Built-in | Built-in |
Not: npm indirme sayılarında Zustand, 2024 ortasından itibaren Redux core'u geçmiştir ve aradaki fark her ay artmaktadır.
Ne Zaman Hâlâ Redux Tercih Edilmeli?
Zustand her senaryo için gümüş kurşun değildir. Şu durumlarda Redux hâlâ mantıklı olabilir:
- Çok büyük ekiplerde Redux'un katı yapısı tutarlılık sağlayabilir
- Mevcut Redux projeleri — Çalışan sistemi göçürmek her zaman mantıklı değildir
- Redux ekosistem araçlarına bağımlılık — RTK Query gibi araçlar çok olgunlaşmış durumda
- Karmaşık saga/effect iş akışları — redux-saga'nın sunduğu gelişmiş pattern'ler
Ancak yeni bir projeye başlıyorsanız, Zustand neredeyse her zaman daha iyi bir başlangıç noktasıdır.
Hızlı Başlangıç: 2 Dakikada Zustand
npm install zustand// store.js
import { create } from 'zustand';
export const useAppStore = create((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}));
// App.jsx
import { useAppStore } from './store';
export default function App() {
const count = useAppStore((s) => s.count);
const increment = useAppStore((s) => s.increment);
return <button onClick={increment}>Sayaç: {count}</button>;
}Gerçekten bu kadar. Provider yok, setup yok, konfigürasyon yok.
Sonuç
Zustand'ın 57.000+ GitHub yıldızına ulaşması ve npm indirmelerinde Redux'u geçmesi bir tesadüf değil. Bunun arkasında çok net bir felsefe var: "State yönetimi bu kadar zor olmak zorunda değil."
Redux, kendi döneminde büyük bir boşluğu doldurdu ve React ekosisteminin olgunlaşmasına katkıda bulundu. Ancak React'in hook'larla evrildiği, basitliğin ve geliştirici deneyiminin ön plana çıktığı bu çağda, Zustand tam olarak geliştiricilerin ihtiyaç duyduğu şeyi sunuyor:
- Minimal API — öğrenmesi dakikalar sürüyor
- Sıfır boilerplate — kod yazarak vakit kazanıyorsunuz
- Provider-free mimari — daha esnek ve test edilebilir
- Mükemmel performans — varsayılan olarak optimize
- Güçlü middleware ekosistemi — gerektiğinde genişletebilirsiniz
Eğer hâlâ Redux ile yeni projeler başlatıyorsanız, kendinize bir iyilik yapın ve Zustand'a bir şans verin. İlk store'unuzu oluşturduğunuzda, "Bu kadar basit olabilir mi?" diye sorduğunuz an, Zustand'ın neden bu kadar popüler olduğunu anlayacaksınız.
State yönetimi zor olmak zorunda değil. Zustand bunu kanıtladı.