Merhaba arkadaşlar, bugün React kullanırken başımı çok ağrıtan ve yeni başlayanların sıklıkla gözden kaçırdığı bir konudan bahsedeceğim: useEffect cleanup. Özellikle API çağrıları veya abonelikler söz konusu olduğunda, bu temizlik işlemini yapmazsanız hafıza sızıntısı (memory leak) kaçınılmaz. Bu hatayı ilk gördüğümde, bileşenler sürekli render olup duruyor ve uygulama gittikçe yavaşlıyordu. İşte benim kullandığım en temiz çözüm.
Sorunun Kökeni Nedir?
React'te `useEffect` hook'u, bileşen DOM'a eklendiğinde (mount) ve belirli bağımlılıklar değiştiğinde çalışır. Ancak bileşen DOM'dan kaldırıldığında (unmount) veya efekt yeniden çalıştırılmadan önce, önceki efektin yarattığı "yan etkileri" (side effects) temizlememiz gerekir. Eğer bir API isteği gönderdiysek ve bileşen anında unmount olduysa, bu istek hala devam edebilir ve sonuç geldiğinde artık var olmayan bir bileşenin state'ini güncellemeye çalışır. İşte bu, memory leak'in ve potansiyel hataların ana kaynağı.
Temizlik Yapmayan Kötü Örnek
Aşağıdaki örnekte, bileşen her render olduğunda bir API'den veri çekiyoruz. Fakat `cleanup` fonksiyonu yok. Eğer kullanıcı bu sayfayı çok hızlı terk ederse veya bileşen çok sık render olursa, tamamlanmamış onlarca istek havada kalacak.
Buradaki fetch isteği, bileşen DOM'dan kaldırıldıktan sonra bile tamamlanmaya çalışır. Sonuç geldiğinde, `setUser` fonksiyonu artık "montajı kaldırılmış" (unmounted) bir bileşende çağrılır. React geliştirme modunda bu durumu size güzel bir hata mesajıyla (`Can't perform a React state update on an unmounted component`) hatırlatır, ama production'da sessizce memory leak'e sebep olur.
Basit ve Etkili Cleanup Çözümüm
Çözüm aslında çok basit: `useEffect` hook'undan bir fonksiyon döndürmek. Bu döndürdüğümüz fonksiyon, bir sonraki efekt çalışmadan ÖNCE veya bileşen unmount olurken ÇALIŞIR. İşte benim tercih ettiğim yöntem:
Buradaki sihir, isMounted değişkeninde. Cleanup fonksiyonu çalıştığında bu değişken `false` olur ve API'den gelen veri artık state'e yazılmaz. Bu, en yaygın ve güvenli yöntemlerden biridir.
Bonus İpucu: Eğer `axios` kullanıyorsanız, `CancelToken` veya `AbortController` ile isteği direkt iptal edebilirsiniz, bu daha da temiz bir çözümdür.
Sonuç olarak, her `useEffect` yazdığınızda kendinize şu soruyu sorun: "Bu efektin temizlenmesi gereken bir şey bırakıyor mu?" Eğer cevap evetse veya emin değilseniz, mutlaka bir cleanup fonksiyonu yazın. Uygulamanızın performansı ve stabilitesi için bu küçük adım büyük fark yaratacak.
Siz React'te benzer bir memory leak sorunu yaşadınız mı? Veya `AbortController` gibi farklı cleanup yöntemleriniz var mı? Yorumlarda deneyimlerinizi paylaşın!
React'te `useEffect` hook'u, bileşen DOM'a eklendiğinde (mount) ve belirli bağımlılıklar değiştiğinde çalışır. Ancak bileşen DOM'dan kaldırıldığında (unmount) veya efekt yeniden çalıştırılmadan önce, önceki efektin yarattığı "yan etkileri" (side effects) temizlememiz gerekir. Eğer bir API isteği gönderdiysek ve bileşen anında unmount olduysa, bu istek hala devam edebilir ve sonuç geldiğinde artık var olmayan bir bileşenin state'ini güncellemeye çalışır. İşte bu, memory leak'in ve potansiyel hataların ana kaynağı.
Aşağıdaki örnekte, bileşen her render olduğunda bir API'den veri çekiyoruz. Fakat `cleanup` fonksiyonu yok. Eğer kullanıcı bu sayfayı çok hızlı terk ederse veya bileşen çok sık render olursa, tamamlanmamış onlarca istek havada kalacak.
JavaScript:
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// Bu istek başlatılıyor...
fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(data => setUser(data)); // Ama bileşen unmount olduysa bu satır hata fırlatabilir!
}, [userId]);
return <div>{user ? user.name : 'Yükleniyor...'}</div>;
}
Çözüm aslında çok basit: `useEffect` hook'undan bir fonksiyon döndürmek. Bu döndürdüğümüz fonksiyon, bir sonraki efekt çalışmadan ÖNCE veya bileşen unmount olurken ÇALIŞIR. İşte benim tercih ettiğim yöntem:
JavaScript:
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// İsteğin devam edip etmediğini takip etmek için bir bayrak (flag)
let isMounted = true;
fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(data => {
// Sadece bileşen hala mount edilmişse state'i güncelle
if (isMounted) {
setUser(data);
}
});
// Cleanup fonksiyonu: Bileşen unmount olduğunda bayrağı false yap
return () => {
isMounted = false;
};
}, [userId]);
return <div>{user ? user.name : 'Yükleniyor...'}</div>;
}
Bonus İpucu: Eğer `axios` kullanıyorsanız, `CancelToken` veya `AbortController` ile isteği direkt iptal edebilirsiniz, bu daha da temiz bir çözümdür.
Sonuç olarak, her `useEffect` yazdığınızda kendinize şu soruyu sorun: "Bu efektin temizlenmesi gereken bir şey bırakıyor mu?" Eğer cevap evetse veya emin değilseniz, mutlaka bir cleanup fonksiyonu yazın. Uygulamanızın performansı ve stabilitesi için bu küçük adım büyük fark yaratacak.
Siz React'te benzer bir memory leak sorunu yaşadınız mı? Veya `AbortController` gibi farklı cleanup yöntemleriniz var mı? Yorumlarda deneyimlerinizi paylaşın!