← Blog
Yazı
· 13 dk· web development

State Management Nedir? React'te State Türleri ve Doğru Yönetim

State management kavramını UI, URL ve server state örnekleriyle öğrenin. useState, Context, Zustand ve TanStack Query arasındaki farkları gerçek bir ürün listesi üzerinden görün.

State Management Nedir?

Bir ürün listesinde arama yaptınız, fiyat filtresi seçtiniz ve ikinci sayfaya geçtiniz. Uygulama, hangi filtrelerin etkin olduğunu, hangi sayfada olduğunuzu ve sunucudan gelen ürünleri bilmek zorunda. Bu bilgilerin tümüne genel olarak state (durum) diyoruz. State management ise bu durumun nerede tutulacağını, nasıl değiştirileceğini ve değiştiğinde arayüzün nasıl güncelleneceğini düzenleme işidir.

Konu yalnızca React'te useState kullanmak değildir. Aynı ekranda farklı ömürlere ve sahiplerine sahip durumlar bulunur. Bir menünün açık olması, paylaşılabilir filtreler ve API'den gelen ürünler aynı yöntemle yönetilmek zorunda değildir.

Kısa cevap

State: Arayüzün o anki görüntüsünü etkileyen değişebilir bilgi.
State management: Bu bilginin sahibini, güncellenme yolunu ve diğer parçalarla eşitlenmesini belirlemek.

Örneğin selectedCategory = "telefon" değişince ürün listesi ve aktif filtre etiketi yeni duruma göre görünmelidir.

React state management yaklasimlari

State Management Cesitleri (Kisaca 3 Ana Grup)

Pratikte state management'i kisa bir modelle 3 ana gruba ayirabilirsiniz:

  • 1. Local UI State: Modal acik/kapali, sekme secimi gibi sadece tek bir bolumu ilgilendiren gecici durumlar.
  • 2. Shared/Global Client State: Tema, kullanici tercihleri veya birden fazla uzak bilesenin paylastigi istemci durumlari.
  • 3. Server State: API'den gelen urunler, siparisler gibi asil kaynagi sunucu olan ve cache/yenileme ihtiyaci olan veriler.

Hizli kural: UI davranislari yerelde, uygulama genel tercihleri paylasilan state'te, veri listeleri ise server state katmaninda kalmali.

State Neden Yönetilmeli?

Küçük bir bileşende durum basittir: düğmeye basılır, sayaç artar, ekran değişir. Uygulama büyüdüğünde aynı bilgi birden fazla bileşeni ilgilendirir. Kategori filtresi hem filtre panelinde hem sonuç başlığında görünür; ayrıca sayfa yenilendiğinde ve bağlantı paylaşıldığında ne olacağına karar vermek gerekir.

Yanlış yere konan state genellikle üç soruna yol açar: iki farklı yerde birbirini tutmayan değerler, gereksiz veri istekleri ve geri/ileri tuşlarıyla bozulan ekranlar. İyi yönetim, her bilgi için bir kaynak (source of truth) seçerek başlar.

State Türleri: Her Bilgi Aynı Yere Ait Değil

TürÖrnekOlası sahip
Yerel UI stateAçık modal, seçili menüuseState veya useReducer
Paylaşılan istemci state'iBirden çok uzak bileşenin kullandığı taslak tercihlerContext veya bir store
URL stateArama, kategori, sayfa numarasıAdres çubuğu / router
Server stateÜrünler, kullanıcı siparişleriSunucu; istemcide istek ve cache katmanı

Bu ayrım katı bir kural değildir. Örneğin bir sekmenin seçimi yalnızca ekrandaki geçici bir tercihse yerel state olabilir; aynı sekmeyi bağlantıyla paylaşmak istiyorsanız URL state daha uygundur.

1. Yerel UI State: Bileşenin Kendi Durumu

Bir modalın açık olup olmadığını sadece o ekran kullanıyorsa state'i ilgili bileşende tutun:

import { useState } from "react";

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

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Yardımı aç</button>
      {isOpen && (
        <div role="dialog" aria-label="Yardım">
          <button onClick={() => setIsOpen(false)}>Kapat</button>
          <p>Ürünleri buradan filtreleyebilirsiniz.</p>
        </div>
      )}
    </>
  );
}

State'i gereksiz yere uygulamanın tepesine taşımak, bu küçük davranışı başka ekranlara bağımlı hâle getirir. Önce onu kullanan en yakın ortak bileşende tutmak genellikle yeterlidir.

2. Paylaşılan İstemci State'i: Birden Fazla Bileşenin İhtiyacı

Bir değer birbirinden uzak bileşenlerde değiştiriliyor ve okunuyorsa paylaşılması gerekebilir. Az sayıda tüketici için ortak parent bileşene taşımak veya Context kullanmak yeterlidir. Uygulama genelinde çok sayıda bağımsız güncelleme ve seçici abonelik gerektiğinde Zustand gibi bir store yararlı olabilir.

Context bir state deposu değildir: Değeri alt bileşenlere ulaştırır; güncelleme mantığı yine useState, useReducer veya başka bir kaynakta yaşar. Ayrıca bir context değerinin değişimi onu okuyan bileşenleri yeniden render edebilir. Sık değişen ve geniş kapsamlı durumları tek büyük context içinde toplamayın.

3. URL State: Paylaşılabilir Ekran Durumu

Şu adresi düşünün: /products?category=phone&page=2. Buradaki kategori ve sayfa numarası yalnızca bir bileşenin geçici tercihi değildir; kullanıcı bağlantıyı kopyaladığında veya tarayıcıda geri tuşuna bastığında da aynı görünüm anlamlı olmalıdır. Bu yüzden URL güçlü bir durum kaynağıdır.

URL'ye çoğunlukla arama terimi, filtre, sıralama ve sayfalama gibi paylaşılması veya geçmişte tutulması gereken değerler konur. Şifre, erişim token'ı ve hassas veriler URL'ye konmaz; URL tarayıcı geçmişinde, loglarda veya paylaşılan bağlantılarda görünebilir.

const url = new URL(window.location.href);
const category = url.searchParams.get("category") ?? "all";
const parsedPage = Number(url.searchParams.get("page"));
const page = Number.isInteger(parsedPage) && parsedPage > 0
  ? parsedPage
  : 1;

// Kategori değiştiğinde mevcut diğer parametreleri koru.
url.searchParams.set("category", "laptop");
url.searchParams.set("page", "1");
window.history.pushState({}, "", url);

Önemli ayrıntı: Yukarıdaki pushState tarayıcı adresini değiştirir; React'e kendiliğinden yeniden render yaptırmaz. Uygulamada URL değişimlerini router'ın sağladığı navigation ve query parametre API'siyle yönetmek daha güvenlidir. URL'deki değerleri okurken geçersiz ve eksik parametreleri de doğrulayın.

4. Server State: Verinin Sahibi Sunucuysa

Ürün listesinin asıl kaynağı API'dir. İstemcideki veri onun bir kopyasıdır; yükleniyor, hata verdi, eski kaldı veya yeniden getirilmeli gibi ek durumları vardır. useState ve useEffect ile istek atılabilir; ancak cache, tekrar deneme ve geçersizleştirme gibi ihtiyaçlar büyüdükçe bunları elle yönetmek zorlaşır.

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

function useProducts(category, page) {
  return useQuery({
    queryKey: ["products", { category, page }],
    queryFn: async ({ signal }) => {
      const params = new URLSearchParams({ category, page: String(page) });
      const response = await fetch('/api/products?' + params, { signal });
      if (!response.ok) throw new Error("Ürünler yüklenemedi");
      return response.json();
    },
  });
}

Burada category ve page URL'den okunabilir; ürünlerin kendisi ise sunucudan gelir. Query key, isteğin hangi filtre ve sayfaya ait olduğunu ifade eder. API verisini ayrıca bir global store'a kopyalamak genellikle iki ayrı doğruluk kaynağı oluşturur.

Derived State: Hesaplanabilen Bilgiyi Tekrar Saklamayın

Bazen arayüzde gördüğünüz bilgi mevcut state'ten hesaplanır. Sepetteki ürünler elinizdeyse toplam adedi ayrıca state olarak tutmanız gerekmez:

const itemCount = cart.reduce((total, item) => total + item.quantity, 0);

Hem cart hem de itemCount bağımsız güncellenirse birbirinden kopabilir. Hesaplama pahalıysa performansı ölçüp memoization değerlendirin; her türetilmiş değer için otomatik olarak useMemo eklemeyin.

useState, Context, Zustand ve TanStack Query: Hangisi Ne İşe Yarar?

  • useState / useReducer: Bileşen veya yakın bileşen grubu içindeki durum; karmaşık geçişlerde reducer yararlı olabilir.
  • Context: Bir değeri bileşen ağacındaki alt bileşenlere iletmek; tema ve oturum bağlamı gibi ihtiyaçlar için.
  • Zustand: Birden çok uzak bileşenin eriştiği, istemciye ait paylaşılan durum için bir store seçeneği.
  • TanStack Query: Sunucu verisini getirme, cache'leme ve güncel tutma süreçleri için.
  • Router / URL: Yenileme, bağlantı paylaşımı ve tarayıcı geçmişiyle yaşaması gereken ekran durumu için.

Bu araçlar birbirinin birebir alternatifi değildir. Bir ürün listesi ekranında modal için useState, filtreler için URL, sonuçlar için TanStack Query ve uygulama çapında bir istemci tercihi için Context veya store aynı anda kullanılabilir.

Pratik Karar Rehberi

  1. Değer başka verilerden hesaplanıyor mu? Evetse çoğu zaman ayrıca saklamayın.
  2. Asıl sahibi sunucu mu? Evetse veri alma ve cache katmanında tutun.
  3. Bağlantıyı paylaşınca aynı ekran açılmalı mı? Evetse URL'yi değerlendirin.
  4. Yalnızca yakın bileşenler mi kullanıyor? Evetse yerel state veya ortak parent yeterli olabilir.
  5. Uzak bileşenler gerçekten aynı istemci durumunu mu paylaşıyor? Evetse Context veya store değerlendirin.

Sık Yapılan Hatalar

  • Her şeyi global store'a koymak: Küçük UI davranışlarını gereksiz yere tüm uygulamaya bağlar.
  • Server state'i global store'a kopyalamak: Hangi kopyanın güncel olduğunu belirsizleştirir.
  • URL ve local state'i aynı filtre için ayrı ayrı kaynak yapmak: Geri/ileri ve sayfa yenilemesinde ekranın farklı değer göstermesine yol açar.
  • Geçersiz URL parametrelerine güvenmek: ?page=-5 gibi değerleri doğrulamamak kırık ekranlar üretebilir.
  • Her değişimi kalıcı saklamak: Bir tooltip'in açık olup olmadığının sayfa yenilenince korunmasına çoğu zaman gerek yoktur.

Sonuç

State management için tek bir kütüphane seçmekten önce, her bilginin sahibini ve yaşam süresini belirleyin. Modalın durumu bileşende, paylaşılabilir filtreler URL'de, API'den gelen ürünler sunucu verisi katmanında durabilir. Böyle kurulduğunda arayüzün neden değiştiğini izlemek, hatayı bulmak ve uygulamayı büyütmek kolaylaşır.

Sonraki adım olarak URL state'i daha derin inceleyebilirsiniz: filtre, arama ve sayfalama parametrelerini React ve Next.js'te yönetirken geri/ileri davranışı ve geçersiz değerleri ele almak başlı başına ayrı bir konudur.