React'te durum (state) yönetimi: useState'ten Zustand'a

React uygulamalarında durum nasıl yönetilir? useState, useReducer, Context API ve üçüncü taraf kütüphaneler (Zustand, Redux) karşılaştırması.

2026-01-1015 dk okuma
React'te durum (state) yönetimi: useState'ten Zustand'a

React uygulamalarının kalbinde veri akışı yatar. Bir bileşenin ne göstereceği, hangi olaylara tepki vereceği ve nasıl davranış sergileyeceği tamamen o bileşenin sahip olduğu veriye — yani state'e — bağlıdır. Yanlış yönetilen bir durum, beklenmeyen yeniden render'lara, hatalara ve bakım kabustan çıkan bir kod tabanına yol açar. Doğru yönetilen bir durum ise uygulamanızı öngörülebilir, test edilebilir ve ölçeklenebilir kılar.

Bu rehberde React'te durum yönetimini temellerinden ileri düzey kalıplara kadar ele alıyoruz. useState'ten useReducer'a, Context API'den Zustand ve Redux'a kadar her aracın ne zaman, neden ve nasıl kullanılacağını somut örneklerle açıklıyoruz.

React'te state nedir ve neden önemlidir?

State, bir bileşenin zaman içinde değişebilen verisidir. Bir sayacın değeri, bir formun doldurulup doldurulmadığı, bir listedeki öğeler, bir kullanıcının oturum bilgisi — tümü state olarak temsil edilir. React, state değiştiğinde bileşeni otomatik olarak yeniden render eder; bu sayede arayüz her zaman güncel veriyle uyumlu kalır.

Ancak bu güç sorumluluk da gerektirir._state'i doğrudan değiştirmeye çalışmak, birden fazla bileşenin aynı veriye erişmesi veya asenkron işlemlerin durumu bozması gibi sorunlar hızla ortaya çıkar. Bu yüzden React, state yönetimini belirli kalıplar ve araçlarla disipline eder.

useState: en temel state aracı

Her React geliştiricisinin ilk tanılaştığı araç useState'tir. Basit, tekil durum değerlerini yönetmek için mükemmeldir.

import { useState } from 'react'

function Sayac() {
  const [sayac, setSayac] = useState(0)

  return (
    <div>
      <p>Değer: {sayac}</p>
      <button onClick={() => setSayac(sayac + 1)}>Artır</button>
      <button onClick={() => setSayac(sayac - 1)}>Azalt</button>
    </div>
  )
}

useState'i doğru kullanmak için ipuçları

Asla state'i doğrudan değiştirmeyin. React, state değişimini yalnızca setter fonksiyonuyla yapar. Doğrudan atama yapmak bileşenin yeniden render edilmesine yol açmaz.

// Yanlış
sayac = sayac + 1

// Doğru
setSayac(sayac + 1)

Fonksiyonel güncelleme kullanın. Bir önceki değere dayalı güncelleme yaparken callback formunu tercih edin. Bu, özellikle asenkron işlemlerde veya çoklu state güncellemelerinde tutarlılık sağlar.

setSayacOnceki => sayacOnceki + 1)

Nesne ve dizi state'lerini doğru kopyalayın. React state'i değiştirmez (mutation); yeni bir referans bekler. spread operatörünü veya Object.assign'ı kullanarak mevcut state'i kopyalayıp güncelleme yapın.

const [kullanici, setKullanici] = useState({ ad: 'Ahmet', soyad: 'Yılmaz' })

// Yanlış — mevcut nesneyi doğrudan değiştirir
kullanici.ad = 'Mehmet'

// Doğru — yeni nesne oluşturur
setKullanici({ ...kullanici, ad: 'Mehmet' })

Ne zaman useState yeterli olur?

  • Tekil değerler (sayı, string, boolean) yönetirken
  • Basit form durumları tutarken
  • Küçük, izole bileşenlerde lokal durum yönetirken
  • Sibling bileşenler arasında veri paylaşımı gerekmediğinde

useReducer: karmaşık durumlar için güçlü bir araç

Bileşeninizde birden fazla alt state parçası varsa, durum geçişleri karmaşıksa veya bir sonraki durumun mevcut duruma bağlıysa useReducer tercih edilmelidir. Redux'tan ilham alan bu API, durum yönetimini merkezileştirir ve tahmin edilebilirliği artırır.

import { useReducer } from 'react'

type Durum = {
  sayac: number
  adimlar: string[]
}

type Eylem = { tur: 'ARTIR' } | { tur: 'AZALT' } | { tur: 'SIFIRLA' } | { tur: 'ISIM_EKLE'; isim: string }

function reducer(durum: Durum, eylem: Eylem): Durum {
  switch (eylem.tur) {
    case 'ARTIR':
      return { ...durum, sayac: durum.sayac + 1, adimlar: [...durum.adimlar, 'Artırıldı'] }
    case 'AZALT':
      return { ...durum, sayac: durum.sayac - 1, adimlar: [...durum.adimlar, 'Azaltıldı'] }
    case 'SIFIRLA':
      return { sayac: 0, adimlar: [] }
    case 'ISIM_EKLE':
      return { ...durum, adimlar: [...durum.adimlar, eylem.isim] }
    default:
      return durum
  }
}

function KontrolPaneli() {
  const [durum, dispatch] = useReducer(reducer, { sayac: 0, adimlar: [] })

  return (
    <div>
      <p>Sayac: {durum.sayac}</p>
      <button onClick={() => dispatch({ tur: 'ARTIR' })}>Artır</button>
      <button onClick={() => dispatch({ tur: 'AZALT' })}>Azalt</button>
      <button onClick={() => dispatch({ tur: 'SIFIRLA' })}>Sıfırla</button>
      <ul>
        {durum.adimlar.map((adim, i) => <li key={i}>{adim}</li>)}
      </ul>
    </div>
  )
}

useReducer'ın avantajları

  • Merkezi mantık: Tüm durum geçişleri tek bir reducer fonksiyonunda toplanır; bu da test etmeyi ve hata ayıklamayı kolaylaştırır.
  • Tahmin edilebilirlik: Eylem türleri net olduğu için hangi durumun ne zaman değişeceği öngörülebilir.
  • Genişletilebilirlik: Yeni durum tipleri ve eylemler eklemek mevcut kodu bozmadan yapılabilir.
  • Yan etkilerden ayrışma: Reducer fonksiyonu saf bir fonksiyondur; yan etkiler useEffect veya dış middleware ile yönetilir.

useState mi useReducer mı?

Basit karar kuralı: durumunuz tek bir değerden oluşuyorsa ve geçişleri basitsa useState kullanın. Birden fazla alt parçası varsa, geçişler karmaşıksa veya durumu bir child bileşene props olarak aktarmak yerine reducer ile merkezileştirmek istiyorsanız useReducer seçin.

Context API: prop drilling'i ortadan kaldırın

Bir üst bileşenin durumunu鸟birden çok alt bileşene aktarmanız gerektiğinde, her seviyede props geçirmek (prop drilling) kodu şişirir ve bakım yükünü artırır. React Context, bu sorunu doğrudan çözmenizi sağlar.

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

type TemaContextType = {
  tema: 'acik' | 'koyu'
  temaDegistir: () => void
}

const TemaContext = createContext<TemaContextType | undefined>(undefined)

function TemaProvider({ children }) {
  const [tema, setTema] = useState<'acik' | 'koyu'>('acik')

  const temaDegistir = () => {
    setTemaOnceki => onceki === 'acik' ? 'koyu' : 'acik')
  }

  return (
    <TemaContext.Provider value={{ tema, temaDegistir }}>
      {children}
    </TemaContext.Provider>
  )
}

function TemaButonu() {
  const ctx = useContext(TemaContext)
  if (!ctx) throw new Error('TemaContext provider içinde kullanılmalıdır')
  return <button onClick={ctx.temaDegistir}>Tema Değiştir</button>
}

Context'in performansı

Context her değiştirildiğinde, o provider'ı sarmalayan tüm alt bileşenler yeniden render edilir. Bu nedenle Context'i çok sık değişmeyen veriler için kullanmak en doğrusudur. Sık değişen durumlar için bir state management kütüphanesi daha uygun olabilir.

Context'i performanslı kullanmak için:

  • Provider değerini memoize edin: useMemo kullanarak value nesnesinin her render'da yeniden oluşmasını engelleyin.
  • Küçük context'lere bölün: Tek bir devasa context yerine,.functional olarak ayrılmış küçük context'ler tercih edin.
  • Sık değişen verileri ayrı tutun: Oturum bilgisi gibi nadiren değişen veriler ile tema gibi daha sık değişen verileri ayrı context'lerde yönetin.

Zustand: minimal ve hızlı bir state management

Zustand, React ekosisteminde son yıllarda büyük popülerlik kazanan, minimal API yüzey alanına sahip bir state management kütüphanesidir. Boilerplatecode gerektirmez, Context'e ihtiyaç duymaz ve son derece performanslıdır.

import { create } from 'zustand'

type Magaza = {
  sayac: number
  artir: () => void
  azalt: () => void
  sifirla: () => void
}

const useMagaza = create<Magaza>((set) => ({
  sayac: 0,
  artir: () => set((durum) => ({ sayac: durum.sayac + 1 })),
  azalt: () => set((durum) => ({ sayac: durum.sayac - 1 })),
  sifirla: () => set({ sayac: 0 }),
}))

function SayacBileseni() {
  const sayac = useMagaza((durum) => durum.sayac)
  const artir = useMagaza((durum) => durum.artir)

  return (
    <div>
      <p>Sayac: {sayac}</p>
      <button onClick={artir}>Artır</button>
    </div>
  )
}

Zustand'ın öne çıkan özellikleri

  • Boilerplate yok: createContext, Provider sarmalama gibi adımlar gerekmez. Store'u oluşturup doğrudan kullanabilirsiniz.
  • Seçici abonelik: useMagaza((d) => d.sayac) yazdığınızda yalnızca sayac değiştiğinde bileşen yeniden render edilir. Bu, performans için kritik bir optimizasyondur.
  • Middleware desteği: devtools, persist, immer gibi middleware'ler entegre edilebilir.
  • Next.js uyumlu: Sunucu tarafı render (SSR) ile uyumlu çalışır, hydration sorunlarını minimal tutar.

Zustand ne zaman tercih edilir?

  • Küçükten orta ölçekli uygulamalarda basit ve hızlı bir çözüm arıyorsanız
  • Redux'un boilerplate yükünü taşımak istemiyorsanız
  • Component-level state'i global state ile karıştırmadan yönetmek istiyorsanız
  • Performans kritikse ve seçici abonelik önemliyse

Redux: büyük uygulamalar için olgun bir ekosistem

Redux, React ekosisteminin en köklü state management çözümüdür. Tek sorumluluk ilkesi (single source of truth), immutable state ve pure reducer mantığı üzerine kuruludur. Redux Toolkit ile birlikte büyük ölçüde basitleşmiştir.

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

const sayacSlice = createSlice({
  name: 'sayac',
  initialState: { deger: 0 },
  reducers: {
    artir: (durum) => { durum.deger += 1 },
    azalt: (durum) => { durum.deger -= 1 },
    ekle: (durum, eylem) => { durum.deger += eylem.payload },
  },
})

export const { artir, azalt, ekle } = sayacSlice.actions

const magaza = configureStore({
  reducer: {
    sayac: sayacSlice.reducer,
  },
})

Redux Toolkit'in avantajları

  • createSlice: Reducer ve action creator'ları otomatik üretir. Immer entegrasyonu ile immutable güncelleme yazımı kolaylaşır.
  • configureStore: Redux DevTools, middleware ve thunk entegrasyonunu tek satırda kurar.
  • createAsyncThunk: Asenkron işlemler için standart bir kalıp sunar.
  • RTK Query: API sorguları için önbellekleme ve senkronizasyon sağlar.

Karşılaştırma: hangi aracı ne zaman seçmeli?

SenaryoÖnerilen AraçNeden
Tek bileşende basit durumuseStateEn minimal çözüm, öğrenmesi kolay
Birden fazla alt parçalı durumuseReducerMerkezi mantık, test edilebilirlik
Derin ağaç paylaşımıContext APIProp drilling'i engeller
Küçük-orta ölçekli uygulamaZustandMinimal, hızlı, boilerplate yok
Büyük kurumsal uygulamaRedux ToolkitOlgun ekosistem, geniş araç desteği
Asenkron veri yönetimiRTK Query veya SWRÖnbellekleme, yeniden deneme, senkronizasyon

Pratik ipuçları: state yönetimi hatalarından kaçınma

1. State'i gereksiz yere üst seviyeye taşımayın. Yalnızca bir bileşinin kullandığı durumu o bileşende tutun. Gereksiz lifting, yeniden render Zincirlerini gereksiz yere büyütür.

2. Asenkron işlemleri reducer içinde yapmayın. Reducer saf olmalıdır. API çağrılarını useEffect, createAsyncThunk veya bağımsız servis fonksiyonlarında yapın, sonucu reducer'a aktarın.

3. Derived state'i store'da tutmayın. Bir listeden filtrelenmiş veri, toplam sayı gibi hesaplamaları store'da saklamak yerine, useMemo ile hesaplayın. Bu, tutarsızlık riskini ortadan kaldırır.

4. State yapınızı düz tutun. Derin iç içe nesneler yerine düz yapıları tercih edin. İhtiyacınız olduğunda normalize edilmiş veri yapısı kullanın — özellikle Redux'ta.

5. Optimistic update uygulayın. Kullanıcı bir işlem yaptığında sunucu yanıtını beklemeden arayüzü güncelleyin; hata olursa geri alın. Bu, kullanıcı deneyimini önemli ölçüde iyileştirir.

6. State management ile veri fetching'i karıştırmayın. Zustand veya Redux sunucudan gelen verileri saklayabilir, ancak bu tür veriler için React Query, SWR veya RTK Query gibi özel araçlar daha uygun olabilir. Bu araçlar önbellekleme, yeniden deneme, yeniden getirme ve senkronizasyon gibi konularda çok daha güçlüdür.

Sonuç

React'te state yönetimi tek bir doğru yolla yapılmaz; her durumun kendine özgü gereksinimleri vardır. useState'in sadeliği, useReducer'ın disiplini, Context'in paylaşım kolaylığı, Zustand'ın minimal performansı ve Redux'un olgun ekosistemi bir arada düşünülmelidir. Önemli olan, uygulamanızın ölçeğine, karmaşıklığına ve ekibinizin deneyimine en uygun aracı seçmek ve tutarlı bir kalıp oluşturmaktır.

Unutmayın: iyi bir state yönetimi yalnızca çalışan bir uygulama değil, aynı zamanda bakım kolaylığı, performans ve test edilebilirlik demektir. Doğru araçları seçerek ve iyi kalıplar uygulayarak, React uygulamanızı uzun vadede sürdürülebilir kılabilirsiniz.

İlgili Yazılar

Okuyucu Yorumlari

5.0 (0 yorum)

Mevcut Yorumlar

Henuz yorum yapilmamis. Ilk siz olun.

Yorum Birakin