Yazılarımız

OfisData

REACT STATE YÖNETİMİ STRATEJİSİ SEÇMEK

React mavi atom logosu yanında 3 farklı state store karar kartı useState Context ve Zustand etiketli minimalist beyaz kompozisyon

"React'ta state yönetimi için ne kullanmalıyız?" sorusunun cevabı 2017'de basitti: Redux. 2024'te çok daha karmaşık. useState, useReducer, Context, Zustand, Jotai, Recoil, Redux Toolkit, MobX, React Query, SWR; her birinin yeri var. Yanlış seçim aşırı karmaşıklık veya yetersiz altyapı yaratır.

State yönetiminin sırrı: tek bir araç hepsini çözmez. Farklı state türleri farklı çözümler ister; useState ve useReducer gibi yerleşik araçların ne için tasarlandığını React öğrenme kaynakları üzerinden görmek, üçüncü parti kütüphane kararını da netleştirir. UI state, server state, URL state, form state, navigation state - hepsi farklı yaklaşım gerektirir.

Doğru state aracı sorusu, doğru state türü sorusunun gölgesidir: local UI state, paylaşılan client state ve server state ayrı dertlerdir ve ayrı araçlar ister. Tür ayrımı oturduğunda modern projelerin tipik mimarisi (useState + hafif global store + query kütüphanesi) kendiliğinden anlamlı hâle gelir.

State türleri

State türüÖrnekTipik çözüm
Local UI stateModal açık, form inputuseState
Shared UI stateTheme, sidebar açıkContext veya Zustand
Server stateAPI'den gelen dataReact Query
URL stateFilter, search queryURL params + router
Form stateInput değerleri, validationReact Hook Form
Global app stateAuth user, cartZustand / Redux

useState: lokal state

Tek component'in kullandığı state. En basit, en hızlı.

function ModalButton() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Aç</button>
      {isOpen && <Modal onClose={() => setIsOpen(false)} />}
    </>
  );
}

Ne zaman: state sadece bu component ve child'larında kullanılıyorsa.

useReducer: kompleks local state

State birden fazla değer içeriyor ve farklı action'lar tetikliyorsa.

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT': return { ...state, count: state.count + 1 };
    case 'RESET': return { count: 0 };
    case 'SET_NAME': return { ...state, name: action.payload };
    default: return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0, name: '' });

  return (
    <>
      <p>{state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
    </>
  );
}

useState'ten daha "yapılandırılmış"; state logic component dışında bir reducer fonksiyonunda. Test kolay; complex state için uygun.

Context: paylaşılan state (az değişen)

Birden fazla component'in eriştiği state için. Ama performans açısından sınırlı.

const ThemeContext = createContext();

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Layout />
    </ThemeContext.Provider>
  );
}

function Header() {
  const { theme } = useContext(ThemeContext);
  return <header className={theme}>...</header>;
}

Ne zaman: theme, locale, auth user gibi az değişen global state. Sık değişen state için Context kullanmak performans sorunu yaratır (her değişiklikte tüm consumer'lar re-render).

Zustand: modern global state

Redux'ın yerini alan basit, hafif kütüphane. 2024'te en popüler tercih.

import { create } from 'zustand';

const useCartStore = create((set) => ({
  items: [],
  addItem: (item) => set((state) => ({
    items: [...state.items, item]
  })),
  removeItem: (id) => set((state) => ({
    items: state.items.filter(i => i.id !== id)
  })),
  clear: () => set({ items: [] })
}));

// Component'te kullanım
function Cart() {
  const items = useCartStore((state) => state.items);
  const removeItem = useCartStore((state) => state.removeItem);

  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          {item.name}
          <button onClick={() => removeItem(item.id)}>Sil</button>
        </li>
      ))}
    </ul>
  );
}

Avantajları:

  • Boilerplate minimal
  • TypeScript desteği mükemmel
  • Selector ile sadece kullanılan state'i izle (re-render optimizasyonu)
  • Provider gerekmez; doğrudan import et kullan
  • Bundle size ~1 KB

Redux Toolkit: kurumsal global state

Redux modern hali. Boilerplate azaltılmış ama hâlâ Zustand'dan daha verbose.

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

const cartSlice = createSlice({
  name: 'cart',
  initialState: { items: [] },
  reducers: {
    addItem: (state, action) => { state.items.push(action.payload); },
    removeItem: (state, action) => {
      state.items = state.items.filter(i => i.id !== action.payload);
    }
  }
});

export const { addItem, removeItem } = cartSlice.actions;
export const store = configureStore({ reducer: { cart: cartSlice.reducer } });

Ne zaman: çok büyük takım, complex state mantığı, time-travel debugging gerektiği, mevcut Redux projesi olduğu durumlarda. Yeni projeler için çoğunlukla overkill.

React Query: server state

API'den gelen veri için ayrı bir araç gerek. useState ile yapılan boilerplate kodu temizler.

import { useQuery } from '@tanstack/react-query';

function ProductList() {
  const { data, isLoading } = useQuery({
    queryKey: ['products'],
    queryFn: fetchProducts
  });

  if (isLoading) return <p>Yükleniyor...</p>;
  return <ul>{data.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}

Server state için detaylar React Query ile server state yönetmek sayfasında.

URL state: router params

Bazı state URL'de tutulmalı. Filter, search query, pagination paylaşılabilir/bookmarklanabilir olmalı.

import { useSearchParams } from 'react-router-dom';

function ProductList() {
  const [searchParams, setSearchParams] = useSearchParams();
  const filter = searchParams.get('filter') || 'all';
  const page = Number(searchParams.get('page')) || 1;

  return (
    <>
      <select
        value={filter}
        onChange={(e) => setSearchParams({ filter: e.target.value, page: '1' })}
      >
        <option value="all">Tümü</option>
        <option value="active">Aktif</option>
      </select>
    </>
  );
}

Avantaj: kullanıcı URL'i kopyalayıp paylaşabilir; sayfa yenilense bile state korunur; tarayıcı geri/ileri butonları çalışır.

State türü karar akış diyagramı diamond karar düğümlerinden useState React Query URL params Context ve Zustand seçeneklerine giden YES NO dallı flowchart

Karar matrisi

Sadece bir component kullanıyor mu?

  • Evet → useState
  • Hayır → sonraki soru

API'dan mı geliyor?

  • Evet → React Query
  • Hayır → sonraki soru

URL'de tutulabilir mi (filter, search)?

  • Evet → URL params + router
  • Hayır → sonraki soru

Sık değişiyor mu?

  • Hayır (theme, locale) → Context
  • Evet → Zustand (veya Redux Toolkit büyük takımlar için)

Modern proje state mimarisi

Tipik bir modern React projesi şu kombinasyonu kullanır:

  • useState: Local UI state (modal, dropdown, input)
  • React Hook Form: Form state
  • React Query: Server state (products, users, posts)
  • URL params: Filter, pagination, search
  • Zustand: Global UI state (cart, theme, auth)
  • Context: Sadece çok az değişen şeyler (i18n)

Redux yok; ama büyük kurumsal projelerde Zustand yerine Redux Toolkit kullanılabilir.

State management yaygın hataları

  1. Her şeyi global yapmak. Local state global'e çekmek karmaşıklık ekler
  2. Server state'i global state'e koymak. Cache, refetch, sync problemleri; React Query daha iyi
  3. Context'i sık değişen state için kullanmak. Performance felaketi
  4. useState yerine useReducer'ı her yerde kullanmak. Basit state için overkill
  5. Form için Redux kullanmak. React Hook Form daha uygun
  6. URL'de tutulabilecek state'i Redux'a koymak. URL daha basit ve paylaşılabilir

State lifting

İki component'in aynı state'e ihtiyacı varsa state ortak parent'a taşınır.

// Yanlış: iki component'te ayrı state
function App() {
  return (
    <>
      <Search /> {/* kendi state'i */}
      <Results /> {/* kendi state'i */}
    </>
  );
}

// Doğru: state parent'ta
function App() {
  const [query, setQuery] = useState('');
  return (
    <>
      <Search query={query} onChange={setQuery} />
      <Results query={query} />
    </>
  );
}

State lifting tipik ilk adım. Çok yukarı çıkarsa Zustand'a alınır; çok aşağıdaysa prop drilling sorunu olabilir, Context çözer.

State persisting (localStorage)

Sayfa yenilense bile korunması gereken state için localStorage.

// Zustand ile
import { persist } from 'zustand/middleware';

const useStore = create(
  persist(
    (set) => ({
      theme: 'light',
      setTheme: (theme) => set({ theme })
    }),
    {
      name: 'app-storage'
    }
  )
);

Tek satırla state otomatik localStorage'a kaydedilir; sayfa açıldığında geri yüklenir. React Query persistance plugin'i de var.

VS Code dark theme Zustand create store kodu items addItem removeItem clear action ve useCartStore selector kullanım örneği syntax vurgulu

İleri Adımlar

State yönetimi modern React'ın en stratejik kararlarından. Pratiği sürekli biçimde geliştirmek için kapsamlı React eğitimi programları hooks ve state mimarisi konularını birlikte aktarır.

Toparlarsak

React state yönetimi tek bir araç değil; state türüne göre seçim. useState lokal UI, useReducer kompleks lokal, Context az değişen paylaşılan, Zustand global UI, React Query server, URL params paylaşılabilir state için. Modern proje karması bunların kombinasyonu. Redux artık yeni projeler için varsayılan değil; Zustand çoğu durum için yeterli. Her şeyi global yapmak en yaygın hata; doğru araç doğru yerde kullanılmalı.

 Vimaj