Bu makalede7 bölüm
- Teknoloji
- React, TanStack Query, SWR, Zustand, Redux Toolkit, React Router
- Konu
- React uygulamasında durum türlerini ayırmak ve araç seçimi
- Seviye
- Orta–ileri
- Ön koşullar
- React hook’ları (useState, useEffect) bilgisi
- REST veya GraphQL API ile çalışma deneyimi
Şişmiş bir Redux store’un içini açtığınızda gördüğünüz şey çoğu zaman istemci durumu değildir: sipariş listeleri, kullanıcı kayıtları, isLoading bayrakları, elle tutulan lastFetched zaman damgaları. Bunların hepsi sunucuda yaşayan verinin kopyasıdır ve store onları tazelemek, tutarlı tutmak için kod yığınına dönüşür. “Hangi state kütüphanesini seçelim?” sorusu bu yüzden yanlış yerden sorulur. Önce durumu türlerine ayırın, araç seçimi arkasından gelir.
Durum türlerini ayırmak
Kütüphane tartışmasına girmeden uygulamadaki her durum parçasını aşağıdaki dört sınıftan birine yerleştirin. Bu ayrım, hangi aracı seçtiğinizden daha belirleyicidir.
| Tür | Gerçek sahibi | Örnek | Uygun araç |
|---|---|---|---|
| Sunucu durumu | Backend / API | Sipariş listesi, kullanıcı profili | TanStack Query, SWR |
| URL durumu | Adres çubuğu | Filtre, sayfa no, sıralama, seçili sekme | Router arama parametreleri |
| Form durumu | Form bileşeni | Düzenlenen alanlar, doğrulama hataları | React Hook Form, yerel state |
| İstemci (UI) durumu | Tarayıcı oturumu | Açık modal, tema, sepet taslağı | useState, Context, Zustand, Redux Toolkit |
Sunucu durumu diğer üçünden temelden farklıdır: başkası değiştirebilir, siz ekrana bakarken eskiyebilir ve asenkron gelir. Onu bir değişken gibi değil, uzak bir kaynağın önbelleği gibi yönetirsiniz. Bir değerin sınıfını bulmak için tek soru yeter: “Sayfayı yenilediğimde bu değer nereden geri gelmeli?” Cevap API ise sunucu durumu, adres çubuğuysa URL durumu, hiçbir yerden gelmemesi gerekiyorsa istemci durumudur. Bu soruyu store’daki her dilime sorun. Tablo kendiliğinden dolar.
Sunucu durumu ve veri önbellekleme kütüphaneleri
TanStack Query ve SWR sunucu verisini bir anahtar (query key) altında önbelleğe alır. Yükleniyor ve hata durumlarını, arka planda yeniden doğrulamayı, aynı anda giden istekleri birleştirmeyi (deduplication) ve değişiklik sonrası geçersiz kılmayı (invalidation) sizin yerinize yönetir. Redux içinde isLoading, error, lastFetched alanlarını elle tutan kodun önemli kısmı bu kütüphanelerle ortadan kalkar.
// TanStack Query v5 ile sipariş listesi ve güncelleme
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
export function useOrders(filters: { status: string; page: number }) {
return useQuery({
queryKey: ['orders', filters], // filtre anahtarın parçasıdır
queryFn: () => api.get('/orders', { params: filters }),
staleTime: 30_000, // 30 sn boyunca taze kabul et
});
}
export function useApproveOrder() {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: string) => api.post(`/orders/${id}/approve`),
onSuccess: () => qc.invalidateQueries({ queryKey: ['orders'] }),
});
}
Bu kodda üç ayar işi belirler. Query key tasarımı: filtreler anahtara girmezse farklı filtrelerin sonuçları birbirinin üzerine yazılır; kullanıcı “Onaylı” sekmesine geçer ama bir an “Açık” siparişleri görür. staleTime: varsayılan sıfırdır, veri hemen “bayat” sayılır ve bileşen yeniden bağlandığında, pencere odağı döndüğünde veya ağ bağlantısı yeniden kurulduğunda arka planda tekrar çekilir. Ülke listesi, ürün kategorileri gibi seyrek değişen referans verilerde bu değeri yükseltmek gereksiz istekleri keser. Geçersiz kılma kapsamı: ['orders'] önekiyle geçersiz kılmak tüm filtre kombinasyonlarını yeniler; daha dar anahtar vermek mümkündür ama tutarsızlık riskini artırır.
Next.js App Router projelerinde verinin bir kısmı Server Component içinde çekilir ve istemciye hiç state olarak gelmez. Bu durumda istemci tarafı sorgu kütüphanesi yalnız etkileşimli bölümlerde gerekir: sonsuz kaydırma, canlı filtre, mutasyon sonrası yenileme. Sınırın nereden çizileceği Next.js App Router’da Server ve Client Component ayrımı rehberinde ayrıntılı anlatılıyor.
İyimser güncelleme ne zaman kullanılmalı?
Onay düğmesi, yıldızlama, sıralama değiştirme gibi anında geri bildirim beklenen işlemlerde iyimser güncelleme (optimistic update) arayüzü sunucu yanıtını beklemeden günceller, hata dönerse geri alır. Tutarın, stokun veya yetkinin değiştiği işlemlerde sunucu yanıtını bekleyin. Kullanıcıya “onaylandı” gösterip iki saniye sonra geri almak iş süreçlerinde güveni sarsar. Para işinde iyimserlik olmaz.
URL ve form durumu
Kurumsal iş uygulamalarının liste ekranlarında filtre, sayfa numarası ve sıralama global store’a konunca üç şikâyet gelir: paylaşılan bağlantı aynı görünümü açmaz, tarayıcının geri tuşu beklendiği gibi çalışmaz, sayfa yenilenince kullanıcı baştan başlar. Destek ekibine düşen “linki gönderdim ama karşı taraf başka liste görüyor” bildiriminin kaynağı genellikle budur. Bu bilgilerin doğal yeri URL’dir.
// React Router v6: filtreyi URL'de tutmak
// (v7'de aynı kanca 'react-router' paketinden de içe aktarılabilir)
import { useSearchParams } from 'react-router-dom';
function OrderFilters() {
const [params, setParams] = useSearchParams();
const status = params.get('status') ?? 'open';
const page = Number(params.get('page') ?? '1');
const { data } = useOrders({ status, page }); // liste ayrı bileşende render edilir
return (
<select
value={status}
onChange={(e) => setParams({ status: e.target.value, page: '1' })}
>
<option value="open">Açık</option>
<option value="approved">Onaylı</option>
</select>
);
}
URL parametreleri metindir. Sayıya, tarihe veya listeye çevirirken geçersiz değeri varsayılana düşürün; elle yazılmış ?page=abc ekranı çökertmemeli. Next.js’te aynı desen useSearchParams ve useRouter ile, App Router’da ayrıca sayfa bileşeninin searchParams özelliğiyle sunucu tarafında kurulur (Next.js 15 ve sonrasında bu özellik bir Promise olarak gelir ve await edilmesi gerekir).
Form durumu kısa ömürlüdür: form açıkken vardır, gönderildikten sonra anlamını yitirir. Her tuş vuruşunu global store’a yazmak gereksiz yeniden render üretir, birden fazla formun açık olduğu ekranlarda da çakışma yaratır. React Hook Form gibi kütüphaneler alan değerlerini bileşen içinde tutar; sunucuya gönderim bir mutasyondur ve sunucu durumu katmanına aittir. Doğrulama kurallarını istemci ve sunucuda ortaklaştırmak form doğrulamasını tek şemayla yönetmek rehberinin konusu.
Global istemci durumu seçenekleri
Üç tür ayrıldıktan sonra geriye kalan gerçek istemci durumu genellikle küçüktür: kullanıcının arayüz tercihleri, tema, açık kenar çubuğu, çok adımlı sihirbazın taslağı, yerel sepet. Seçenekler şunlar:
useState/useReducerve durumu yukarı taşımak: Durumu birkaç bileşen kullanıyorsa ilk tercih budur. Karmaşık geçişler varsauseReducermantığı test edilebilir kılar.- Context: Bir değeri ağaç boyunca prop geçirmeden dağıtma mekanizmasıdır, state yönetim kütüphanesi değildir. Context değeri değişince onu okuyan tüm bileşenler yeniden render edilir. Tema, dil, oturum bilgisi gibi seyrek değişen değerlerde uygundur; sık değişen değerde performans sorunu çıkarır.
- Zustand: Küçük API’li, provider gerektirmeyen bir store. Bileşenler seçici (selector) ile yalnız ihtiyaç duydukları parçaya abone olur, ilgisiz değişiklikte render tetiklenmez.
- Redux Toolkit: Katı tek yönlü veri akışı, Redux DevTools ile zaman yolculuğu, middleware ekosistemi ve RTK Query ile entegre sunucu durumu yönetimi sunar. Büyük ekipte öngörülebilirlik ve denetlenebilirlik verir; karşılığında daha fazla yapı ister.
// Zustand: seçiciyle abone olmak
import { create } from 'zustand';
type UiState = { sidebarOpen: boolean; toggleSidebar: () => void };
export const useUi = create<UiState>()((set) => ({
sidebarOpen: true,
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}));
// Bileşen yalnızca sidebarOpen değişince render olur
const open = useUi((s) => s.sidebarOpen);
Kütüphane karşılaştırma tablosu
| Ölçüt | Context | Zustand | Redux Toolkit |
|---|---|---|---|
| Ek bağımlılık | Yok | Küçük | Orta (RTK + react-redux) |
| Seçici ile kısmi abonelik | Yok (Context bölünerek) | Var | Var (useSelector) |
| DevTools / iz sürme | React DevTools | Middleware ile Redux DevTools | Yerleşik |
| Sunucu durumu desteği | Yok | Yok (Query ile birlikte) | RTK Query |
| Uygun ölçek | Seyrek değişen birkaç değer | Küçük–orta istemci durumu | Büyük ekip, karmaşık iş kuralları |
Karar sırası pratikte şöyle işler: önce durumun sınıfını belirleyin; sunucu durumunu sorgu kütüphanesine, URL durumunu router’a verin. Kalan istemci durumu tek ekranın içindeyse yerel state yeter. Uygulama genelinde ve seyrek değişiyorsa Context, sık değişiyor ve çok bileşen okuyorsa Zustand uygundur. Ekip Redux Toolkit’e zaten yatırım yapmışsa ve RTK Query ile sunucu durumunu aynı çatıda tutmak istiyorsa, kütüphane değiştirmek yerine mevcut yapıyı sadeleştirmek daha ucuza gelir. Yeniden yazım genellikle en pahalı yoldur.
API tarafındaki seçimin istemci önbelleğine etkisi için REST mi GraphQL mi karşılaştırmasına bakın; GraphQL istemcileri (ör. Apollo Client) kendi normalleştirilmiş önbelleğini getirdiği için ayrı bir sorgu kütüphanesine gerek kalmayabilir.
Aşırı global durumdan kurtulma adımları
Yıllar içinde büyümüş bir kurumsal web uygulamasının Redux store’unu tek seferde yeniden yazmak, canlı sistemde çekirdek switch’i mesai saatinde değiştirmeye benzer. Aşağıdaki sıra özelliği bozmadan ve geri dönüş yolunu açık tutarak ilerler.
- Envanter çıkarın. Store’daki her dilimi (slice) listeleyin ve dört sınıftan birine atayın. Çoğu projede listelerin, detay kayıtlarının ve yükleniyor bayraklarının sunucu durumu olduğu çıkar.
- Sorgu kütüphanesini yanına kurun. TanStack Query’nin
QueryClientProvider’ını ReduxProvider’ı ile birlikte ekleyin. İki sistem bir süre yan yana çalışabilir. - Okuma uçlarını tek tek taşıyın. En basit liste ekranından başlayın: thunk + reducer + selector üçlüsünü bir
useQuerykancasıyla değiştirin. Dilimi silmeden önce başka bir yerin onu okumadığını kod aramasıyla doğrulayın. - Mutasyonları taşıyın. Kaydetme ve onaylama işlemlerini
useMutation’a alın; başarı sonrası store’u elle güncellemek yerine ilgili sorguları geçersiz kılın. - Filtreleri URL’ye çıkarın. Store’daki filtre ve sayfalama bilgisini arama parametrelerine taşıyın; derin bağlantı ve geri tuşu davranışı kendiliğinden düzelir.
- Kalan istemci durumunu küçültün. Geriye birkaç UI dilimi kaldıysa Redux’ta bırakmak ya da Zustand’a taşımak ekip tercihidir. Hedef kütüphane değil, sade bir durum modelidir.
Sık yapılan hatalar
- Sunucu verisini kopyalamak: Sahada en sık gördüğümüz hata budur.
useQuerysonucunuuseEffectiçinde ayrı bir state’e yazmak iki gerçeklik kaynağı yaratır ve biri mutlaka geride kalır. Türetilmiş değeri render sırasında hesaplayın veyaselectseçeneğini kullanın. - Türetilebilen değeri saklamak: Toplam tutar, filtrelenmiş liste, seçili öğe sayısı başka state’ten hesaplanabiliyorsa ayrıca tutulmaz.
- Tek dev Context: Kullanıcı, tema, bildirim ve sepeti aynı Context değerine koymak, birinin değişmesiyle tüm uygulamanın yeniden render olmasına yol açar. Context’leri sorumluluğa göre bölün.
- Query key’de eksik parametre: İstek fonksiyonunun kullandığı her değişken anahtarda yer almalıdır; TanStack Query’nin ESLint eklentisi (
@tanstack/eslint-plugin-query) bu hatayı yakalayan bir kural içerir.
Taşıma sürerken ölçmeden ilerlemeyin. React DevTools Profiler ile kritik ekranlarda render sayısını ve süresini önce ve sonra karşılaştırın; tarayıcının ağ sekmesinde aynı uca giden yinelenen istekleri arayın. Aynı uç bir sayfa açılışında üç kez çağrılıyorsa önbellek anahtarı yanlıştır. Durum katmanındaki tip tanımlarının ve klasör sınırlarının ekip genelinde tutarlı kalması için TypeScript kod tabanı standartları rehberindeki modül sınırı yaklaşımını bu çalışmayla birlikte uygulayın.
Mevcut bir React uygulamasının durum katmanını sadeleştirmek veya yeni projede bu ayrımı baştan kurmak için React ve Next.js web uygulamaları kapsamında mimari değerlendirme yapıyoruz. Kavramsal arka plan için React dokümantasyonundaki Managing State bölümü ve TanStack Query dokümantasyonu birincil kaynaklardır.
Sık sorulan sorular
Redux artık gereksiz mi?
Hayır. Sunucu durumu sorgu kütüphanesine taşınınca Redux’ta kalan iş azalır. Karmaşık istemci tarafı iş kuralları, zaman yolculuğuyla hata ayıklama ihtiyacı ve büyük ekipte katı yapı isteyen projelerde Redux Toolkit hâlâ mantıklı bir seçimdir.
TanStack Query ile SWR arasında nasıl seçim yapılır?
İkisi de sunucu durumunu önbelleğe alır ve arka planda yeniden doğrular. SWR daha küçük bir API sunar; TanStack Query mutasyon, geçersiz kılma, sonsuz sorgu ve DevTools tarafında daha kapsamlıdır. Mutasyonun yoğun olduğu iş uygulamalarında TanStack Query genellikle daha az elle kod ister.
Context performans sorunu yaratır mı?
Context değeri değişince onu okuyan tüm bileşenler yeniden render edilir. Tema veya dil gibi seyrek değişen değerlerde sorun çıkmaz. Sık değişen ve çok bileşenin okuduğu değerlerde Context’i bölün ya da seçici destekleyen bir store kullanın.
Filtreleri neden URL’de tutmalıyım?
URL’deki filtre paylaşılan bağlantıda korunur, tarayıcının geri ve ileri tuşlarıyla doğru çalışır, sayfa yenilenince kaybolmaz. Store’da tutulan filtre bu davranışların hiçbirini kendiliğinden sağlamaz.
- Teknoloji
- React, TanStack Query, SWR, Zustand, Redux Toolkit, React Router
- Konu
- React uygulamasında durum türlerini ayırmak ve araç seçimi
- Seviye
- Orta–ileri
- Ön koşullar
- React hook’ları (useState, useEffect) bilgisi
- REST veya GraphQL API ile çalışma deneyimi
Modern Web Uygulamaları