Merhaba arkadaşlar, bugün React'te useEffect içinde async/await kullanırken başımı çok ağrıtan bir sorundan ve bulduğum temiz çözümden bahsedeceğim. Bu hatayı ilk gördüğümde, component unmount olduktan sonra setState çağrıldı diye console'da kırmızı uyarılar görmekten kafayı yemiştim. Özellikle API isteklerinde, component artık ekranda yokken state güncellemeye çalışmak memory leak ve beklenmeyen davranışlara yol açıyor.
Karşılaştığım Sorun: Unmount Edilmiş Component'e SetState
Klasik async/await yaklaşımımız şöyleydi:
Burada sorun şu: fetch işlemi devam ederken kullanıcı sayfadan ayrılabilir, component unmount olabilir. Ama promise hala çözülmeye (resolve) çalıştığı için, cleanup fonksiyonu olmadığından, setVeri fonksiyonu çağrılmaya devam ediyor. İşte bu, React'in bize şikayet ettiği "Memory leak" ve "Can't perform a React state update on an unmounted component" hatasının temel sebebi.
Çözüm Pattern'im: AbortController + Bir Flag
İşte benim kullandığım en temiz ve etkili çözüm. Bu pattern, hem fetch isteğini iptal etmeye hem de async işlemden dönen promise'i "göz ardı etmeye" yarıyor.
Neden Bu Pattern İşe Yarıyor?
Buradaki sihir iki katmanlı:
1. AbortController: Modern fetch API'si ile gelen bu nesne, isteği sunucu tarafında da durdurabilir (mümkünse). Cleanup tetiklendiğinde abort() çağrılır ve fetch promise'i bir AbortError ile reject edilir.
2. isMounted Flag'i: AbortController bazı eski API'ler veya başka async işlemler (setTimeout, başka kütüphaneler) için yeterli olmayabilir. Bu basit boolean flag, cleanup sırasında false yapılarak, try/catch bloğu içindeki tüm state güncellemelerinin koşula bağlanmasını sağlar. Bu, ekstra bir güvenlik katmanıdır.
Sonuç olarak, component unmount olduğunda hem istek iptal edilir hem de iptal edilmemiş olsa bile state asla güncellenmeye çalışılmaz. Temiz, güvenli ve sürdürülebilir bir kod.
Siz de React'te async işlemlerde benzer memory leak sorunları yaşadınız mı? useEffect cleanup'larınızı nasıl yönetiyorsunuz? Belki sizin de farklı bir pattern'iniz vardır, yorumlarda paylaşabilirsiniz!
Klasik async/await yaklaşımımız şöyleydi:
JavaScript:
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.ornek.com/veri');
const data = await response.json();
setVeri(data); // Eğer component bu sırada unmount olduysa, HATA!
};
fetchData();
}, []);
Burada sorun şu: fetch işlemi devam ederken kullanıcı sayfadan ayrılabilir, component unmount olabilir. Ama promise hala çözülmeye (resolve) çalıştığı için, cleanup fonksiyonu olmadığından, setVeri fonksiyonu çağrılmaya devam ediyor. İşte bu, React'in bize şikayet ettiği "Memory leak" ve "Can't perform a React state update on an unmounted component" hatasının temel sebebi.
İşte benim kullandığım en temiz ve etkili çözüm. Bu pattern, hem fetch isteğini iptal etmeye hem de async işlemden dönen promise'i "göz ardı etmeye" yarıyor.
JavaScript:
import { useEffect, useState } from 'react';
function VeriBileşeni() {
const [veri, setVeri] = useState(null);
const [yukleniyor, setYukleniyor] = useState(true);
useEffect(() => {
// 1. AbortController ve "mounted" flag'ini oluştur.
const abortController = new AbortController();
let isMounted = true;
const fetchData = async () => {
try {
setYukleniyor(true);
const response = await fetch('https://api.ornek.com/veri', {
signal: abortController.signal, // İptal sinyalini fetch'e bağla
});
if (!response.ok) throw new Error('Ağ hatası');
const jsonData = await response.json();
// 2. Component hala mount edilmiş mi, kontrol et!
if (isMounted) {
setVeri(jsonData);
setYukleniyor(false);
}
} catch (error) {
// 3. Hata, isteğin MANUEL iptalinden (AbortError) kaynaklanıyorsa, yakala ama state'i güncelleme.
if (error.name !== 'AbortError' && isMounted) {
console.error('Fetch hatası:', error);
setYukleniyor(false);
// Hata state'inizi burada güncelleyebilirsiniz.
}
}
};
fetchData();
// 4. CLEANUP FONKSİYONU: Burada her iki iptal mekanizmasını da tetikliyoruz.
return () => {
abortController.abort(); // Fetch isteğini iptal et
isMounted = false; // Flag'i false yaparak, gelecek tüm state güncellemelerini engelle
};
}, []); // Boş dependency array: sadece mount'ta çalış
return (
<div>
{yukleniyor ? <p>Yükleniyor...</p> : <p>{JSON.stringify(veri)}</p>}
</div>
);
}
Buradaki sihir iki katmanlı:
1. AbortController: Modern fetch API'si ile gelen bu nesne, isteği sunucu tarafında da durdurabilir (mümkünse). Cleanup tetiklendiğinde abort() çağrılır ve fetch promise'i bir AbortError ile reject edilir.
2. isMounted Flag'i: AbortController bazı eski API'ler veya başka async işlemler (setTimeout, başka kütüphaneler) için yeterli olmayabilir. Bu basit boolean flag, cleanup sırasında false yapılarak, try/catch bloğu içindeki tüm state güncellemelerinin koşula bağlanmasını sağlar. Bu, ekstra bir güvenlik katmanıdır.
Sonuç olarak, component unmount olduğunda hem istek iptal edilir hem de iptal edilmemiş olsa bile state asla güncellenmeye çalışılmaz. Temiz, güvenli ve sürdürülebilir bir kod.
Siz de React'te async işlemlerde benzer memory leak sorunları yaşadınız mı? useEffect cleanup'larınızı nasıl yönetiyorsunuz? Belki sizin de farklı bir pattern'iniz vardır, yorumlarda paylaşabilirsiniz!