React Uygulamalarında Modern State Yönetimi ve Entegrasyon Saklama Yöntemleri

React ekosisteminde, uygulama durumunu (state) yönetmek ve harici servislerle entegrasyonları güvenli şekilde saklamak için çeşitli modern yaklaşımlar vardır. Aşağıda, en popüler state yönetim kütüphanelerinin karşılaştırması, proje dosya yapısı için en iyi pratikler, üçüncü parti entegrasyonların anahtar ve oturum yönetimi, ve örnek bir kredi sistemi senaryosunda asenkron state yönetimi ele alınmıştır.

Modern State Yönetim Yaklaşımları

Geleneksel olarak Redux kullanılmaktaydı, ancak artık Redux’un daha kolay bir sürümü olan Redux Toolkit ve daha hafif alternatifler olan Zustand, Recoil ve Jotai gibi kütüphaneler öne çıkıyor. Her birinin farklı avantaj ve dezavantajları vardır:

Redux Toolkit (RTK)

Redux Toolkit, Redux kütüphanesinin resmi önerilen kullanım şeklidir ve Redux’un karmaşıklığını azaltır. RTK ile slice adı verilen modüler yapılar kullanılır ve Immer sayesinde immutability (değişmezlik) otomatik sağlanır. Redux hâlâ tek bir merkezi store kullanır ve eylemler (action) ile predictable (tahmin edilebilir) bir akış sunar (zustand vs @reduxjs/toolkit vs jotai vs recoil vs valtio | State Management Libraries Comparison) (zustand vs @reduxjs/toolkit vs jotai vs recoil vs valtio | State Management Libraries Comparison). Bu sayede state değişimleri izlenebilir ve güçlü geliştirici araçları (Redux DevTools) ile zaman yolculuğu ile debug imkânı vardır.

Zustand

Zustand, çok küçük boyutlu (≈1.7kb) ve hızlı bir global state yönetim kütüphanesidir. Redux’un fikirlerini basitleştirilmiş şekilde kullanır; ancak Redux gibi Provider ile sarmalamaya gerek olmadan, React dışı bir store yaratır ve React hook’ları ile bu store’a erişilir (zustand/docs/getting-started/comparison.md at main · pmndrs/zustand · GitHub) (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Zustand genellikle tek bir obje olarak state tutar (isterseniz birden fazla store da tanımlayabilirsiniz) ve doğrudan güncellenebilir bir API sunar.

Recoil

Recoil, Facebook ekibi tarafından deneysel olarak çıkarılmış bir state kütüphanesidir. Atom ve Selector kavramları ile çalışır: Atom’lar bağımsız state parçalarıdır, Selector’lar ise atomlardan türetilen (veya asenkron hesaplama yapan) durumlardır. Recoil ile birden fazla atom ve selector arasında bağımlılıklar tanımlayabilir, böylece React’in kendi reaktif yapısı dışında bir reaktif veri grafiği oluşturabilirsiniz. Uygulama genelinde kullanmak için <RecoilRoot> ile sarmalamanız gerekir.

  • Avantajları: State’i ince taneli atomlara bölerek her biri değiştiğinde sadece o atomu kullanan bileşenleri yeniden render eder. Bu sayede büyük global state kullanımında gereksiz render’lar azalır. Derived state kavramı (selector) ile başka atomların kombinasyonundan otomatik hesaplanan değerler tutmak kolaydır (zustand vs @reduxjs/toolkit vs jotai vs recoil vs valtio | State Management Libraries Comparison). Recoil ayrıca React’in Suspense ve ErrorBoundary mekanizmalarıyla doğal uyum içindedir; bir selector içerisinde Promise döndürerek veriyi asenkron çekebilir ve komponentleri bekletip bir loading fallback’i gösterebilirsiniz (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Yani asenkron veri yükleme durumunu yönetmek için ayrı loading değişkenleri tutmaya gerek kalmadan, Suspense ile otomatik bekleme sağlamak Recoil ile mümkündür.

  • Dezavantajları: Henüz resmi 1.0 sürümüne ulaşmamıştır ve geniş çapta üretim kullanımında Redux kadar denenmiş sayılmaz. Facebook tarafından kullanılmasına rağmen topluluk desteği görece küçüktür. Mevcut büyük bir projeye sonradan entegre etmesi zor olabilmektedir (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Ayrıca Recoil atomları string bazlı bir key ile tanımlanır ve bir Context provider’ı gerekli kıldığı için, Jotai gibi daha hafif alternatifler varken bazı geliştiriciler tarafından tercih kaybına uğramıştır. Yine de konsept olarak Jotai’ye ilham vermiş, yenilikçi bir yaklaşımdır.

Jotai

Jotai (Japonca’da “durum, state” anlamına gelir), Recoil’den esinlenen ancak onu daha minimal ve esnek hale getiren bir kütüphanedir. Atomlar üzerine kuruludur: her atom tek bir state parçasını temsil eder ve React’in useAtom hook’u ile hem değere hem de onu değiştirecek sete erişirsiniz. Jotai’de global bir store objesi yoktur; state, ihtiyaca göre context içinde veya modül seviyesinde tanımlanabilir (Comparison — Jotai, primitive and flexible state management for React). Varsayılan (provider’sız) modda tüm uygulama için tek bir görünmez context kullanır, ancak isterseniz <Provider> ile state kapsamını sınırlayabilirsiniz (Comparison — Jotai, primitive and flexible state management for React).

Özetle, Redux Toolkit uzun süreli tecrübe ve tek bir merkezi düzen arayan ekipler için güvenli bir seçimdir ancak biraz şablon kod ve kural gerektirir. Jotai ve Zustand ise daha az kurallı, daha esnek ve hızlı çözümler olarak öne çıkmaktadır. Nitekim bir karşılaştırmalı deneydeki yorumda, “kişisel projelerde Jotai veya Zustand’a daha meyilli olacağı” çünkü bunların kurulumu kolay ve Redux Toolkit’ten daha esnek olduğu belirtilmiştir (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks) (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks). Her birinin tercih edilmesi gereken durumlar vardır ve “en iyi” seçim proje gereksinimlerine ve ekibin tercihlerine göre değişir (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks).

Proje Dosya Yapısında En İyi Pratikler

Bir React projesinde dosya yapısını düzenlemek, özellikle state yönetimi kodunu organize etmek açısından kritiktir. Geçmişte Redux ile “actions/ - reducers/ - components/” şeklinde ayrıştırılan klasör yapıları kullanılıyordu, ancak modern pratikler farklıdır. Önerilen en iyi yaklaşım, özellik bazlı (feature-based) modüler bir yapı kullanmaktır.

Redux ekibinin resmi dokümanları da “feature folder” modelini tavsiye eder: Yani her özelliğin (veya domain’in) kendi klasörü olup, o özelliğe ait Redux slice’ı, aksiiyonları ve React bileşenleri aynı yerde bulunur (Code Structure | Redux). Bu modelde örneğin features/urunListesi/urunSlice.ts ve UrunListesi.jsx gibi dosyalar birlikte yer alır. Böylece bir özelliğin tüm ilgili kodu tek yerde tutulur, bakımı ve ölçeklendirmesi kolay olur (Code Structure | Redux). Redux Toolkit ile gelen “ducks” prensibi (reducer ve aksiyonların tek dosyada tanımlanması) bu yapıya uygundur.

Diğer state yönetim araçları için de benzer prensipler uygulanabilir:

  • Zustand veya Jotai kullanırken, eğer state’leriniz birden fazla konsept içeriyorsa, bunları ayrı dosyalara/klasörlere bölmek iyi bir fikirdir. Örneğin authStore.ts, productStore.ts gibi ayrı Zustand store’ları tanımlayabilir veya Jotai atomlarını ilgili modüllerinde oluşturabilirsiniz. Büyük bir uygulamada tek bir devasa store yerine, mantıksal olarak ayrılmış parçalar olması okunabilirliği artırır. Bir geliştirici tecrübesine göre, “her özellik için ayrı store oluşturuyorum” şeklinde bir yaklaşım oldukça işe yarar (File structure for a "zustand" powered react project? : r/reactjs). Bu, özellikle farklı store’lardan türetilen değerler gerekiyorsa, birleştirme mantığını özel hook’lar içine koymaya da imkân tanır (File structure for a "zustand" powered react project? : r/reactjs).

  • Küçük/orta ölçekli projelerde src/store/ ya da src/state/ adıyla bir klasör altında tüm global state yönetimi dosyalarını toplamak da görülür. Örneğin store/auth.ts, store/cart.ts vb. Bu yaklaşım, proje henüz çok büyümemişken düzen sağlar. Ancak proje büyüdükçe yine feature bazlı bölümlere geçmek faydalı olabilir.

  • State yönetimi kodunu bileşen kodundan ayrı tutmak önemlidir, fakat tamamen kopuk olmamalı. Örneğin bir bileşenin kendine özel bir context veya Zustand store’u varsa, bunu bileşenin dosyalarının yanına koyabilirsiniz. Bazı durumlarda, “çok sayıda yerden etkileşime girilen bir bileşen (örneğin bildirimler)” kendi store’unu yanında tutmak mantıklı olabilir, böylece o bileşenin bir parçası gibi davranır (File structure for a "zustand" powered react project? : r/reactjs). Fakat genele hitap eden (auth gibi) state parçaları üst seviye bir konumda tanımlanmalıdır ki tüm uygulama erişebilsin (File structure for a "zustand" powered react project? : r/reactjs).

Bir örnek yapı olarak şunu verebiliriz:

src/
├─ app/               # Uygulama genelini ilgilendiren ayarlar (store konfigurasyonu vs.)
├─ common/            # Ortak bileşenler, yardımcı fonksiyonlar, hooklar
├─ features/
│   ├─ auth/
│   │    ├─ authSlice.ts   # veya authStore.ts (kullanılan state kütüphanesine göre)
│   │    ├─ AuthPage.jsx
│   │    └─ ... 
│   ├─ products/
│   │    ├─ productsSlice.ts
│   │    ├─ ProductsList.jsx
│   │    └─ ...
│   └─ ... (diğer özellik klasörleri)
└─ index.js / App.js   # Giriş noktası, Provider sarımları vs.

Yukarıdaki yapıda her özelliğin klasörü içinde o özelliğe dair React komponentleri ve gerekirse state yönetim kodu (slice, store, atom vs.) bulunur. Böyle bir düzen, farklı ekip üyelerinin aynı anda farklı özellikler üzerinde çalışmasını kolaylaştırır ve kodun anlaşılabilirliğini artırır (Code Structure | Redux) (Code Structure | Redux).

Özetle, tek bir “stores” klasörü kullanmak yerine özellik bazlı modüller kullanmak günümüzde daha yaygın ve ölçeklenebilir bir yöntemdir. Elbette proje yapısı konusunda katı bir kural yoktur; önemli olan ekibinizin kolay anlayacağı ve sürdürebileceği bir düzen kurmaktır. Tutarlı olduğunuz sürece, hangi yöntemi seçtiğinizden ziyade seçtiğiniz yöntemi disiplinli uygulamak daha kritiktir.

Üçüncü Parti Entegrasyonların Yönetimi (API Anahtarları ve Oturumlar)

Modern web uygulamaları sıklıkla Google, Apple, Facebook, Instagram, Notion gibi harici servislerle bütünleşir. Bu entegrasyonlar API anahtarları, OAuth istemci kimlikleri, erişim token’ları gibi hassas bilgiler gerektirebilir. Bu verilerin yönetimi hem güvenlik hem de uygulama mimarisi açısından dikkat istemektedir:

1. API Anahtarlarını Saklama: Genellikle harici servislerin API anahtarları ve gizli bilgilerinin kaynak kodda düzgün şekilde gizlenmesi gerekir. React uygulamalarında bunun standard yolu environment variable (.env) kullanmak ve bu dosyayı versiyon kontrolüne eklememektir. Örneğin Create React App tabanlı bir projede .env dosyasına REACT_APP_GOOGLE_API_KEY=... şeklinde bir anahtar koyup, uygulamada process.env.REACT_APP_GOOGLE_API_KEY ile kullanabilirsiniz. Bu yöntem, anahtarları kod dışında tutarak repoya girmesini önler (Understanding React Environment Variables: An Essential Guide). Ancak unutulmamalıdır ki, ön uç (front-end) tarafında bulunan environment variable değerleri derleme sırasında koda gömüleceği için, tamamen gizli kalmaz. Hiçbir zaman gerçek sırlar (secret) doğrudan front-end’e konmamalıdır. Nitekim Create React App dokümanında da şu uyarı yapılır: “Uyarı: Özel anahtarlarınızı React uygulamanızda tutmayın! Ortam değişkenleri derlemeye dâhil edilir, uygulamanızın dosyalarını inceleyerek herkes onları görebilir.” (reactjs - How to correctly store secret keys on React App? - Stack Overflow). Bu nedenle:

  • Genel API anahtarları (örneğin Google Maps gibi sadece domain kısıtlamasıyla korunan, herkese açık anahtarlar) front-end’te bulunabilir ama yine de .env üzerinden gelmelidir.

  • Özel/Secret anahtarlar (örneğin istemci sırrı, API secret) asla front-end koduna konmamalıdır. Bu tür işlemler için bir arka uç (backend) servisi veya proxy katmanı kullanılması en güvenli yoldur. Örneğin, Notion API’siyle entegre olmak için server tarafında bir endpoint koyup oradan Notion token’ını kullanmak gerekebilir.

2. OAuth ve Kimlik Doğrulama Entegrasyonları: Google, Apple, Facebook gibi sağlayıcılarla kimlik doğrulama (login) sağlanırken genelde OAuth 2.0/OpenID Connect protokolü kullanılır. Bu akışlarda front-end tarafı bir access token veya ID token alır. Bu token’ların saklanması ve yönetimi kritik bir konudur:

  • Token’ı Uygulama State’inde Tutma: En basit yöntem, kullanıcı giriş yaptığında alınan token’ı bir global state’de (Redux store, Context ya da Zustand store gibi) tutmaktır. Bu sayede uygulamanın farklı yerlerinde oturum bilgisine erişilebilir. Ancak yalnızca state’e koymak yeterli olmaz; sayfa yenilendiğinde state sıfırlanacağı için oturum devamlılığı için token’ı kalıcı bir yerde saklamak gerekir (Web Security: localStorage vs cookie for storing tokens - DEV Community).

  • Local Storage / Session Storage: Token’ı tarayıcıda saklamanın yaygın yolu localStorage veya sessionStorage kullanmaktır. Bu yöntem, sayfa yenilemelerinde token’ın korunmasını sağlar. Fakat güvenlik açısından dezavantajı, localStorage’daki veriye JS kodu ile erişilebilmesidir. XSS (Cross-Site Scripting) açığı oluşursa saldırgan localStorage’daki token’ı çalabilir (Web Security: localStorage vs cookie for storing tokens - DEV Community). Bu nedenle localStorage’a token koymak görece risklidir.

  • HTTP-Only Cookie: En güvenli yöntemlerden biri, token’ı sunucunun bir HttpOnly cookie’sine yazmasıdır. HttpOnly flag’li cookie’lere JavaScript erişemez, bu yüzden XSS durumunda bile token’a ulaşılamaz (Web Security: localStorage vs cookie for storing tokens - DEV Community). Ayrıca cookie’lere Secure ve SameSite gibi özellikler ekleyerek çalınma ve kötüye kullanım risklerini azaltabilirsiniz. Bu yöntem genellikle bir backend gerektirir (token’ı aldıktan sonra yanıt set-cookie ile dönecek). Eğer mümkünse, hassas oturum token’larını HttpOnly cookie ile saklamak en iyi uygulama olarak kabul edilir.

  • Refresh Token / Expiry: Uzun süreli oturum için refresh token mekanizması kullanılıyorsa, refresh token da benzer şekilde güvenli saklanmalıdır (genelde o da HttpOnly cookie ile). Access token süresi dolunca uygulama refresh token ile yeni bir access token alabilir.

  • Ortam Değişkenleri: OAuth istemci ID gibi gizli olmayan yapılandırmalar yine .env içinde tutulabilir. Örneğin REACT_APP_GOOGLE_OAUTH_CLIENT_ID front-end’te bulunabilir (zaten Google OAuth client id gizli değildir). Ancak client secret kesinlikle front-end’te olmaz.

3. Entegrasyon Kodunun Organizasyonu: Üçüncü parti entegrasyonlarla haberleşen kod genellikle API çağrıları yapar (fetch/axios ile). Bu kodu yönetirken:

  • Her entegrasyon için ayrı bir servis veya yardımcı modül oluşturmak iyi bir pratiktir. Örneğin services/firebaseAuth.js, api/googleDriveApi.ts gibi dosyalar ilgili API ile iletişimi kapsülleyebilir. Bu modüller içinde ilgili anahtar veya endpoint URL’lerini environment variable’dan alıp kullanırlar.

  • Bu servis fonksiyonları içinden elde edilen veriler veya token’lar, global state’e uygun şekilde aktarılabilir. Örneğin loginWithGoogle() fonksiyonu Google OAuth akışını yapıp token döner, siz de Redux store’da authSlice içindeki loginSuccess(token) action’ını çağırırsınız.

  • Oturum Yönetimi: Kullanıcının login/logout durumu, uygulamada global bir state olarak tutulmalıdır. Genelde user objesi ya da isAuthenticated flag’i global state’de (Redux, Context vs) yer alır. Bu state, uygulamanın nav bar gibi birçok yerine etki ettiği için global olmalıdır. Örneğin Redux kullanıyorsanız, authSlice state’inde user: null | {…} şeklinde tutulabilir. Zustand/Jotai kullanıyorsanız benzer şekilde useAuthStore veya userAtom tanımlanabilir.

  • Güvenlik İpuçları: API anahtarlarını front-end’te barındırırken mümkün mertebe erişim yetkilerini kısıtlayın. Örneğin Google API key’iniz sadece belirli domain’lerden istek yapabilir olsun, Facebook App ID’niz sadece belirli yönlendirme URI’larını kabul etsin vs. Bu şekilde anahtarınız ifşa olsa bile kullanılamayacaktır (Best Practices for API Key Safety | OpenAI Help Center). Ayrıca, entegrasyonları yaparken üretici dokümanlarının en iyi güvenlik pratiklerini takip etmek gerekir.

Özetle, popüler entegrasyonlarda gizlilik ve güvenlik en önemli konudur. API anahtarlarını koddan ayrı (env ile) tutmak, kullanıcı token’larını güvenli şekilde saklamak (tercihen HttpOnly cookie ile) ve uygulama state’ine yalnızca gerekli bilgileri koyup kalanını gerektiğinde çekmek en iyi yaklaşımlardandır. Bu sayede hem kullanıcı oturumları sorunsuz yönetilir hem de hassas bilgiler korunmuş olur.

Kredi Sistemi Gibi Yapılarda Asenkron State Yönetimi (Loading & Güncellemeler)

Bir uygulamada kredi sistemi gibi, kullanıcının bakiyesinin gösterildiği ve harcama/yükleme yaptıkça güncellenen bir senaryoyu ele alalım. Bu tarz yapılar, global state kullanımının ve asenkron işlemlerin iyi modellenmesini gerektirir:

1. Eyalet (State) Tasarımı: Kredi miktarı, muhtemelen kullanıcı oturumunun bir parçasıdır. Bu nedenle global bir state’de (örn. Redux’ta auth.balance veya ayrı bir walletSlice) tutulmalıdır ki uygulamanın farklı yerlerinde (profil, ödeme sayfası vs.) aynı anda görülebilsin. State tasarımında genellikle sadece sayısal bakiye değil, gerekirse durum bilgileri de yer alır: Örneğin balance, loading, error gibi alanlar. Başlangıçta balance: 0, loading: false, error: null gibi bir nesne yapısı kullanılabilir. Bu yapı, bir isteğin durumunu takip etmek için yararlıdır.

2. Asenkron Veri Yükleme ve Güncelleme: Kullanıcının kredi bakiyesi muhtemelen sunucudan gelir. Uygulama ilk yüklendiğinde mevcut bakiyeyi API’dan çekmek gerekir. Bir satın alma yaptığında da yeni bakiye API’dan döner. Bu süreçte:

  • Loading (Bekleme) Durumları: Bir bakiye güncellemesi çağrısı yapıldığında loading state’i true yapılır, böylece arayüzde “Lütfen bekleyin” spinner’ı gösterilebilir veya ilgili buton devre dışı bırakılabilir. İşlem bittiğinde loading false yapılır. Redux gibi bir yapı kullanıyorsanız bu, dispatch edilen action’larla yapılır (örn. updateCreditsPending, updateCreditsFulfilled gibi). Redux Toolkit ile createAsyncThunk kullanıldığında, thunk otomatik olarak .pending, .fulfilled, .rejected action tipleri üretir; böylece loading ve error durumlarını reducer’da kolayca yakalayabilirsiniz. Yine de bu bir miktar şablon kod demektir ve klasik Redux’ta her istek için üç ayrı action yazmak gerekebilir (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering).

  • Hata Yönetimi: API çağrısı başarısız olursa error state’ine mesaj veya kod konur, UI’da gösterilir. Bu da ilgili catch bloğu veya rejected action ile işlenir.

  • State Güncellemesi: API başarıyla döndüğünde yeni bakiye state’e yazılır. Redux reducer’ınızda state.balance = action.payload.newBalance yapabilirsiniz (RTK, Immer kullandığı için bu tür doğrudan atamalara izin verir). Zustand kullanıyorsanız set(state => ({ balance: newBalance, loading: false })) şeklinde state’i güncellersiniz. Jotai’de balanceAtom’a setBalance(newBalance) yapabilirsiniz. Bu güncelleme sonrası, uygulamada bakiye bilgisini kullanan tüm bileşenler otomatik olarak yeni değeri gösterecektir – state yönetim kütüphanelerinin temel faydası budur.

3. Asenkron İşlemlerin Modellenmesi – Karşılaştırmalı Yaklaşımlar:

  • Redux Toolkit: Asenkron işlemler için ya thunk middleware’ı (RTK’da createAsyncThunk) ya da RTK Query kullanılabilir. RTK Query, Redux store içerisinde veri önbelleği (cache) tutarak API çağrılarını yönetir ve kendi loading/error durumlarını barındırır. Bu sayede manuel action yazmadan, RTK Query’nin otomatik hooks veya slice yapısıyla kredi bakiyesini çekebilirsiniz. Örneğin useGetBalanceQuery() hook’u size { data: balance, isLoading, error } döner. Bu yaklaşım, Redux kullanırken loading ve error state’lerini yönetme yükünü azaltır. Aksi halde thunk ile şu pattern uygulanır: dispatch(fetchBalance()) -> reducer loading=true yapar -> API yanıt geldiğinde dispatch(fetchBalanceSuccess(data)) -> reducer balance günceller, loading=false yapar; hata için benzer şekilde fetchBalanceError.

  • Zustand: Zustand’da asenkron aksiyonlar oldukça düz şekilde yazılır. Örneğin store’unuzda bir fetchBalance: async () => { set({ loading: true }); try { ... set({ balance: data, loading: false }); } catch(e){ set({ error: e, loading: false }); } } fonksiyonu tanımlayabilirsiniz. Yani direkt olarak async fonksiyon içinde set çağırarak state’i güncellersiniz – özel bir yapıya ihtiyaç yoktur, JavaScript’in doğal akışını kullanırsınız. Bu özgürlük esneklik sağlar, ancak manuel yapıldığı için her işlemde benzer kod tekrarı olabilir. Bazı geliştiriciler bu durumda React Query gibi kütüphaneleri Zustand ile birlikte kullanmayı tercih ediyor; öyle ki TanStack Query’yi zustand store’una bağlayan örnekler mevcut (Simplifying Data Fetching with Zustand and Tanstack Query). Bu kombinasyon, data fetching işini React Query’ye bırakıp, sonucu Zustand’a koymak şeklinde oluyor.

  • Recoil: Recoil’in öne çıktığı nokta, asenkron state’i Suspense ile yönetebilmesidir. Örneğin bir balanceSelector tanımlayıp içinde const response = await fetch(...); return response.balance; yaptığınızda, bu selector bir Promise döndürür. Recoil, React componentinden useRecoilValue(balanceSelector) çağrılınca Promise’i yakalayıp bir hata fırlatarak (throw) Suspense mekanizmasını tetikler. Uygulama bu sayede bekleme moduna girer ve tanımladığınız araya girer (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Veri gelince Promise çözülür ve component normal şekilde değeri alır. Bu model, loading state’i tutmayı React seviyesinde halleder. Recoil ile alternatif olarak manuel de yapabilirsiniz (atom + ayrı loading atomu), fakat Suspense varken ihtiyaç azalır. Recoil’de birden fazla atom’un birbiriyle bağımlı asenkron hesapları da selector’lar aracılığıyla kurulabilir (örn. bakiye ve döviz kuru atomlarından türetilen TL karşılığı gibi).

  • Jotai: Jotai de benzer şekilde asenkron atomları destekler. Bir atom’un değerini bir Promise olarak tanımlarsanız, o atomu okuyan componentler Suspense ile bekleyebilir. Ayrıca Jotai’nin resmi extensions paketinde atomWithQuery gibi yardımcılar vardır; TanStack React Query ile entegre şekilde async işlemleri kolaylaştırır (Query — Jotai, primitive and flexible state management for React) (Query — Jotai, primitive and flexible state management for React). Örneğin const balanceAtom = atomWithQuery(getBalanceQuery) diyerek, React Query’nin caching ve refetch özelliklerini Jotai atomuna sarmalayabilirsiniz. Bu sayede loading, error durumlarını React Query halleder ve atom size güncel veriyi sağlar.

  • Context + useReducer (Yerleşik yöntem): React’in built-in araçlarıyla da yapılabilir. Bir context state’i tanımlayıp, reducer ile “LOADING/ SUCCESS/ ERROR” action tiplerini ele alabilirsiniz. Bu aslında Redux’un elle uygulanmış hali gibidir. Küçük uygulamalarda çalışsa da büyüdükçe her async işlem için ayrı kontrol yazmak gerekir ve kod karmaşası artabilir (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Ayrıca context’in optimize edilmesi zordur (her güncellemede tüm tüketen bileşenler render olabilir). Bu yüzden yukarıdaki kütüphaneler genelde tercih edilir.

4. Örnek Güncelleme Akışı: Diyelim kullanıcı arayüzünde “Kredi Satın Al” butonuna bastı:

  1. Bu buton bir aksiyonu tetikler (buyCredits(amount)).

  2. Aksiyon ilk olarak state’de loading=true yapar, varsa hata bilgisini temizler.

  3. Ardından API çağrısı başlar (örneğin POST /api/buy-credits { amount }).

  4. Yanıt gelene kadar UI’da spinner gösterilir, buton kilitlenir (loading durumuna göre UI davranışı).

  5. Yanıt başarılıysa yeni bakiye bilgisi state’e yazılır (balance = newBalance, loading=false).

    • Bu noktada, kredi bakiyesini gösteren tüm bileşenler otomatik olarak yeni değeri gösterir.

  6. Eğer hata olduysa state’de error doldurulur, loading=false yapılır. UI’da hata mesajı gösterilir.

  7. Kullanıcı başka bir işlem yapabilir, veya hatayı kapatıp tekrar deneyebilir; bu durumda error state’i temizlenmelidir (örn. yeni bir aksiyonla veya yeni request başlarken).

Bu akışta optimistic update de yapabilirsiniz: Yani sunucu yanıtını beklemeden önce geçici olarak UI’da bakiyeyi güncellemek. Örneğin, harcama yaparken hemen geçici olarak bakiyeden düşüp, sonra sunucudan onay gelmezse geri almak. Redux veya Zustand ile bunu kontrol etmek sizin elinizde; React Query gibi kütüphaneler ise optimistic update desteğini de kolaylaştırır (useMutation’ın onMutate ve onError fonksiyonları ile).

5. State Güncellemelerinin Yayılımı: Global state kullanmanın avantajı, bir yerde yapılan değişikliğin tüm ilgili yerlere yansımasıdır. Kredi sistemi örneğinde, kullanıcı bakiyesi güncellendiğinde, hem üst menüdeki “Bakiyeniz: X” yazısı hem de hesap sayfasındaki bakiye aynı anda güncellenecektir. Bunu sağlamak için ek bir şey yapmanız gerekmez; tek yapmanız gereken state’i merkezi yerde değiştirmektir. Redux’ta reducer içinde yeni state’i döndürmek, Zustand’da set çağırmak veya Jotai’de atom’u yazmak bu güncellemeyi tetikler. Bu kütüphaneler, reaktif bir şekilde abonelik mekanizması kurduğundan, ilgili komponentler değişimi otomatik algılar ve yeniden render olur. Böylece manuel DOM güncelleme derdiniz olmaz, uygulama durumu her zaman tek kaynaktan doğru kabul edilir.

6. Performans ve Ölçeklenebilirlik: Kredi sistemi gibi sık güncellenen bir veri söz konusuysa, performans konusuna da dikkat etmek gerekir. Eğer her küçük değişimde tüm uygulama yeniden render oluyorsa bu verimsiz olur. Modern state yönetim kütüphaneleri bunu minimize edecek araçlar sunar:

  • Redux’ta slice’ları ayrıştırmak ve component seviyesinde useSelector kullanmak, sadece o slice değişince ilgili componenti yeniler. Derin nested state değişimlerinde bile, immutable yapı sayesinde değişmeyen kısımlar referans eşit kalır ve React Redux bunlar için render tetiklemez.

  • Zustand/Jotai, state’i parçalara böldüğünüzde aynı etkiyi sağlar. Jotai’de her atom bağımsızdır, değişmeyen atom değişti diye diğerini etkilemez. Zustand’da useStore(selector, shallow) gibi bir kullanım, yalnız seçilen alan değiştiğinde render olmayı sağlar.

  • Recoil’de atom’lar zaten granular olduğundan benzer şekilde performans dostudur.

Sonuç olarak, asenkron işlemler ve loading/hata durumları her ne kadar ek state değişkenleri gerektirse de, doğru araç ve pattern’lerle yönetilebilir. Redux tarafında biraz daha elle kontrol ve boilerplate varken (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering), Recoil/Jotai gibi kütüphaneler React’in mekanizmalarını kullanarak bu süreçleri basitleştirebilir (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Ayrıca React Query / RTK Query gibi araçlarla entegre olarak, krediler gibi sunucu kaynaklı verileri cache’leyip güncellemeyi oldukça kolay hale getirebilirsiniz. Önemli olan, uygulamanın her durumu için (bekliyor, başarı, hata) kullanıcıya uygun geri bildirim vermeyi ve state’i tutarlı güncellemeyi sağlamaktır.


Kaynaklar: Modern state yönetim kütüphanelerinin dokümanları ve karşılaştırmaları (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering) (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks), Redux resmi stil rehberi (Code Structure | Redux), deneyimli geliştirici blogları (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks) (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks) ve güvenlik konularında uzmanlaşmış makaleler (reactjs - How to correctly store secret keys on React App? - Stack Overflow) (Web Security: localStorage vs cookie for storing tokens - DEV Community) esas alınmıştır. Bu yöntemler sektörde kendini kanıtlamış pratikler olup, projenizin büyüklüğüne ve ihtiyaçlarına göre en uygun kombinasyonu seçebilirsiniz.

Last updated

Was this helpful?