React bileşenlerinde state yönetimi, kullanıcı etkileşimlerine tepki vermenin temel yoludur. useState hook'u, fonksiyonel bileşenlerde state tanımlamayı ve güncellemeyi sağlar. Peki bu basit görünen hook'u kullanırken hangi hatalardan kaçınmalısınız? Bu yazıda useState'in doğru kullanımını, sık yapılan hataları ve performans iyileştirme ipuçlarını bulacaksınız.
useState Hook'u Nedir ve Nasıl Çalışır?
useState, React'in fonksiyonel bileşenlere state eklemenizi sağlayan bir hook'tur. Kullanımı şu şekildedir:
const [state, setState] = useState(initialValue);
Burada state mevcut değeri, setState ise state'i güncellemek için kullanılan fonksiyondur. İlk render'da initialValue ile başlar. Her setState çağrısı bileşenin yeniden render edilmesine neden olur.
State Güncelleme: Değer mi Yoksa Fonksiyon mu Kullanılmalı?
setState'e doğrudan yeni bir değer verebilirsiniz:
setCount(count + 1);
Ancak state güncellemesi asynchronous olduğu için, aynı anda birden fazla güncelleme yapılırsa eski değer (stale state) kullanılabilir. Bu durumda fonksiyonel güncelleyici kullanmak daha güvenlidir:
setCount(prevCount => prevCount + 1);
Fonksiyonel güncelleyici, her zaman en güncel state değerini alır ve sıralı güncellemelerde doğru sonucu verir.
Başlangıç Değerini Fonksiyonla Vermek: Lazy Initial State
Başlangıç değeri hesaplaması pahalı bir işlemse (örneğin localStorage'dan veri okuma), bunu doğrudan useState'e parametre olarak vermek her render'da tekrar çalışmasına neden olur. Bunun yerine lazy initializer kullanın:
const [data, setData] = useState(() => {
const stored = localStorage.getItem('data');
return stored ? JSON.parse(stored) : [];
});
Bu fonksiyon sadece ilk render'da bir kez çalışır, böylece gereksiz hesaplama önlenir.
Obje ve Dizi State'lerini Güncellerken Yapılan Hatalar
React'te state immutable olmalıdır. Yani mevcut state'i doğrudan değiştiremezsiniz. Örneğin bir dizi state'ine yeni eleman eklerken:
// YANLIŞ
setItems(items.push(newItem)); // items.push orijinal diziyi değiştirir
// DOĞRU
setItems([...items, newItem]); // yeni bir dizi oluştur
Aynı şekilde obje güncellemelerinde de spread operator veya Object.assign kullanın:
setUser({...user, name: 'Ali'});
State Güncellemesi Hemen Etki Etmez mi?
setState çağrısından hemen sonra state'in güncellendiğini varsaymak yanlıştır. React performans için birden fazla state güncellemesini gruplayabilir. Bu nedenle aşağıdaki gibi bir kod beklendiği gibi çalışmayabilir:
setCount(count + 1);
console.log(count); // hala eski değeri gösterir
Yeni state değerini kullanmanız gerekiyorsa useEffect kullanın veya güncelleme fonksiyonunun içinde hesaplama yapın.
Birden Fazla State Değişkeni mi Tek Bir Obje mi?
Eğer state'ler birbiriyle ilişkiliyse tek bir obje kullanmak daha mantıklı olabilir. Ancak her değişiklikte tüm objeyi güncellemek gerektiğinden, bağımsız değişkenler için ayrı useState kullanmak daha iyidir. Karar verirken şu tablodan yararlanabilirsiniz:
| Durum | Önerilen Yaklaşım |
|---|---|
| State'ler bağımsız | Her biri için ayrı useState |
| State'ler sık sık birlikte güncelleniyor | Tek bir obje useState |
| State derin iç içe | useReducer veya Immer kütüphanesi |
Performans İpuçları: Gereksiz Render'ları Önleme
Her state güncellemesi bileşenin yeniden render edilmesine neden olur. Aşırı render'ları önlemek için:
- State'i mümkün olduğunca aşağı (alt bileşenlere) taşıyın.
- Büyük objeleri state olarak tutmaktan kaçının; sadece gerekli alanları tutun.
- Aynı değeri tekrar setState ile atamaktan kaçının; React yine de render tetikler.
Sık Yapılan Hatalar ve Çözümleri
- Hata: State'i doğrudan değiştirmek (mutation).
Çözüm: Her zaman yeni bir referans oluşturun. - Hata: Fonksiyonel güncelleyici yerine sabit değer kullanmak.
Çözüm: Güncelleme bir önceki state'e bağlıysa fonksiyon kullanın. - Hata: setState'i bir döngü içinde çağırmak.
Çözüm: Güncellemeleri tek seferde yapın veya fonksiyonel güncelleyici ile sıralayın.
useState vs useReducer: Ne Zaman Hangisini Kullanmalı?
Basit state'ler için useState idealdir. Ancak state yapısı karmaşık veya birden çok alt değer içeriyorsa, veya state güncelleme mantığı birbirine bağlıysa useReducer daha uygundur. Örneğin bir form state'i için useReducer daha okunabilir kod sağlar.
Özet
useState, React'in en temel hook'udur ancak doğru kullanılmadığında beklenmedik hatalara yol açabilir. Bu yazıda state güncelleme, lazy initialization, immutability ve performans konularını ele aldık. Bu ipuçlarını uygulayarak daha güvenilir ve verimli React bileşenleri yazabilirsiniz.
Sık Sorulan Sorular
useState'te başlangıç değeri fonksiyon olarak verilirse ne olur?
Başlangıç değeri bir fonksiyon olarak verildiğinde (lazy initial state), bu fonksiyon sadece ilk render'da bir kez çalışır ve dönen değer state'in ilk değeri olur. Bu sayede pahalı hesaplamalar her render'da tekrarlanmaz.
setState neden asenkron çalışır?
React, performansı artırmak için birden fazla state güncellemesini tek bir render'da gruplar (batching). Bu nedenle setState çağırdıktan hemen sonra state'in güncellendiğini varsaymak yanlıştır. Yeni değere ancak bir sonraki render'da erişilebilir.
Dizi state'ine nasıl eleman eklenir?
Mevcut diziyi değiştirmeden (immutable) yeni bir dizi oluşturarak ekleme yapmalısınız. Örneğin: setItems(prev => [...prev, newItem]) veya setItems([...items, newItem]) kullanabilirsiniz.
useState ile useReducer arasındaki fark nedir?
useState basit state değerleri için idealdir. useReducer ise karmaşık state mantığı, birden çok alt değer veya state güncellemelerinin birbirine bağlı olduğu durumlarda daha uygundur. useReducer daha merkezi bir güncelleme mantığı sağlar.
setState'te aynı değer tekrar atanırsa render tetiklenir mi?
Evet, teknik olarak React aynı değer atandığında bile render'ı tetikleyebilir, ancak React 18'de bu durumda render iptal edilir (bail out). Yine de performans için aynı değeri tekrar setState ile atamaktan kaçınmak önerilir.
