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.
Avantajları: Yaygın ve olgun bir ekosisteme sahiptir; büyük topluluk ve çok sayıda orta yazılım (middleware) desteği mevcuttur. Redux yıllardır kullanıldığı için ekiplerin adapte olması genellikle kolaydır (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks). RTK ile klasik Redux’a göre daha az kod yazılır ve iyi bir proje yapısı ile ölçeklenebilir.
Dezavantajları: Diğer modern alternatiflere kıyasla hâlâ nispeten fazla kod ve ayar gerektirebilir. Tek bir kök (root) store’a tüm durumu koyma yaklaşımı çok büyük uygulamalarda esneklik sorunları yaratabilir (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks). Asenkron işlemler için ek araçlar (thunk, saga veya RTK Query) kullanmanız gerekir. Özellikle elle yönetiliyorsa, her istek için loading ve error state’lerini izlemek için fazladan efor gerekir (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Yani yüklenme durumları ve hatalar için özel aksiyonlar ve reducer kodları yazmak tipiktir.
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.
Avantajları: API’si son derece basit ve kullanımı kolaydır (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Redux’taki gibi dispatch veya action tanımlamaya gerek kalmaz; store içinde tanımladığınız setter fonksiyonlarını doğrudan çağırarak state’i güncellersiniz. Bu da minimum boilerplate demektir. Bundle boyutunun küçük olması ve performansının yüksek olması önemli artılardır (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Ayrıca Redux DevTools ile entegre olabilir, böylece debug edilebilir. Geliştirici deneyimi olarak “her şeyi yapabileceğiniz bir meta-platform” gibidir; farklı pattern’leri kendi içinde uygulayabilme esnekliğine sahip olup birçok uç durumu kendisi halleder (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks). Küçük ve orta ölçekli projeler için popüler bir tercihtir ve daha büyük projelere de dikkatli bir yapılandırmayla uyarlanabilir.
Dezavantajları: React Context gibi atomik bir yapı yerine tek bir store objesi kullandığı için, kontrolsüz kullanılırsa bir state değişimi gereksiz birçok komponenti yeniden render edebilir. Bunu önlemek için Zustand’ın selector pattern’ini kullanarak her komponentin sadece ihtiyacı olan parçayı alması önerilir (tıpkı Redux’ta
useSelector
kullanır gibi). Dokümantasyonunun bazı konularda yetersiz olduğu eleştirileri vardır (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering), ancak community örnekleri bunu kapatmaya yardımcı oluyor. Genel olarak büyük bir eksisi olmamakla beraber, kapsamlı bir ekosistemi Redux kadar yoktur (ama React Query gibi kütüphanelerle beraber kullanılabilir).
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).
Avantajları: API’si useState kadar basit ve sezgiseldir, öğrenme eğrisi düşüktür. Her bir state değeri ayrı atom olduğu için, componentler sadece kullandıkları atom değişince yeniden render olur – bu da React Context kullanımında görülen ekstra render sorununu çözer (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Ekstra memoization uğraşlarını azaltır. Jotai’nin çekirdek paketi son derece küçüktür (~3 KB boyutunda) ve TypeScript desteği kusursuzdur (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). State’inizi kod içerisinde dağıtık (atomic) hale getirip konumlandırmak kolaydır; hangi bileşene aitse orada tanımlanabilir, böylece veri ile onu kullanan UI yakın durur (Redux Toolkit vs. Jotai vs. Zustand — React state managers comparison | Tom Tech Talks). Türetilmiş atomlar ile başka atomlardan hesaplanan değerler oluşturmak mümkündür ve hatta atom’lar içinde
useReducer
benzeri yan etkili işlemler de yapılabilir (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Suspense desteği ile asenkron atomlar tanımlanabilir. Eklenti paketleri sayesinde TanStack React Query entegrasyonu gibi güçlü özellikler ekleyebilirsiniz (örn.atomWithQuery
,atomWithMutation
gibi yardımcılar mevcuttur).Dezavantajları: Recoil’e kıyasla bazı gelişmiş özelliklere sahip olmayabilir (5 State management for React. Recoil vs. Jotai vs. Zustand vs. Redux… | by Amanda G | Product Engineering). Örneğin, Recoil’in geliştirme araçları veya bazı eşzamanlı mod özellikleri Jotai’de yoktur (veya harici çözümlerle yapılır). Topluluk büyüklüğü orta seviyededir ancak hızla artmaktadır. Çok sayıda atomla çalışırken yönetim karmaşıklaşırsa, disiplinli bir yapı kurmak gerekir (örneğin atomları ait oldukları özellik modüllerinde tutmak gibi). Yine de Jotai’nin esnekliği sayesinde bu dezavantajlar çoğu proje için tolere edilebilirdir.
Ö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 dasrc/state/
adıyla bir klasör altında tüm global state yönetimi dosyalarını toplamak da görülür. Örneğinstore/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:
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’daauthSlice
içindekiloginSuccess(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 daisAuthenticated
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’indeuser: null | {…}
şeklinde tutulabilir. Zustand/Jotai kullanıyorsanız benzer şekildeuseAuthStore
veyauserAtom
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ğindeloading
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 ilecreateAsyncThunk
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ızset(state => ({ balance: newBalance, loading: false }))
şeklinde state’i güncellersiniz. Jotai’debalanceAtom
’asetBalance(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())
-> reducerloading=true
yapar -> API yanıt geldiğindedispatch(fetchBalanceSuccess(data))
-> reducerbalance
günceller,loading=false
yapar; hata için benzer şekildefetchBalanceError
.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çindeset
ç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çindeconst response = await fetch(...); return response.balance;
yaptığınızda, bu selector bir Promise döndürür. Recoil, React componentindenuseRecoilValue(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ğinconst 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ı:
Bu buton bir aksiyonu tetikler (
buyCredits(amount)
).Aksiyon ilk olarak state’de
loading=true
yapar, varsa hata bilgisini temizler.Ardından API çağrısı başlar (örneğin
POST /api/buy-credits { amount }
).Yanıt gelene kadar UI’da spinner gösterilir, buton kilitlenir (loading durumuna göre UI davranışı).
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.
Eğer hata olduysa state’de
error
doldurulur,loading=false
yapılır. UI’da hata mesajı gösterilir.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?