Foruma hoş geldin 👋, Ziyaretçi

Forum içeriğine ve tüm hizmetlerimize erişim sağlamak için foruma kayıt olmalı ya da giriş yapmalısınız. Foruma üye olmak tamamen ücretsizdir.

Bir component unmount olduktan sonra state update'i yapmaya çalışmanın memory leak uyarısı ve çözümü

pixero

Üye
Katılım
14 Mart 2026
Mesajlar
20
Merhaba arkadaşlar, bugün başımı çok ağrıtan ve özellikle React'ta asenkron işlemler yaparken hepimizin karşısına çıkma ihtimali yüksek olan bir sorundan bahsedeceğim. Bu hatayı ilk gördüğümde kafayı yemiştim, çünkü her şey doğru gibi görünüyordu ama konsol sürekli kırmızı uyarılarla doluyordu.

🔥 Karşılaştığım Sorun

Bir API'den veri çektiğimiz klasik bir senaryo düşünün. Kullanıcı bir butona tıklıyor, siz de `useEffect` içinde veya bir event handler'da `fetch` veya `axios` ile istek atıyorsunuz. Veri gelene kadar component'in kullanıcı tarafından başka bir sayfaya geçiş yapmasıyla DOM'dan kaldırıldığını (unmount) hayal edin. İşte tam da bu anda, gelen cevabı işlemeye çalışan kod, artık DOM'da olmayan bir component'in state'ini güncellemeye kalkışıyor.

React bize hemen şu meşhur uyarıyı fırlatıyor: "Can't perform a React state update on an unmounted component." Bu bir memory leak (bellek sızıntısı) uyarısıdır. Çünkü component yok olduğu halde, onun state'ini değiştirmeye çalışan bir fonksiyon hala çalışıyor ve bu da gereksiz bellek kullanımına ve potansiyel hatalara yol açıyor.

⚙️ Temel Problemli Kod Örneği

İşte bu soruna davetiye çıkaran tipik bir kod parçası:

JavaScript:
import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const fetchUser = async () => {
      setLoading(true);
      // Diyelim ki bu istek yavaş çalışıyor...
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      // Eğer kullanıcı bu sırada sayfadan ayrıldıysa, component unmount olur.
      // Aşağıdaki satırlar çalıştığında artık bu component DOM'da YOK!
      setUser(data); // ⚠️ BURASI HATA FIRLATIR!
      setLoading(false);
    };

    fetchUser();
  }, [userId]); // userId değişince yeniden çalışır

  if (loading) return <p>Yükleniyor...</p>;
  return <div>{user ? user.name : 'Kullanıcı bulunamadı'}</div>;
}

Gördüğünüz gibi, `fetchUser` fonksiyonu `await` ile cevabı bekliyor. Bu sırada component kaldırılırsa, gelen `data` ile `setUser` ve `setLoading` fonksiyonları çağrılmaya çalışılıyor. İşte felaket tam da burada başlıyor.

✅ Kullandığım En Temiz Çözüm: Cleanup Function

React'ın bize bu tür sorunlar için harika bir çözümü var: `useEffect` hook'unun cleanup (temizleme) fonksiyonu. Bu fonksiyon, component unmount olmadan HEMEN önce veya dependency'ler değiştiğinde ve effect yeniden çalıştırılmadan önce çalıştırılır. Amacımız, bu cleanup anında, devam eden asenkron işlemleri iptal etmek veya en azından state güncellemesini engellemek.

İşte benim kullandığım en temiz çözüm:

JavaScript:
import { useState, useEffect, useRef } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  // Component'in mount durumunu takip etmek için bir ref kullanıyoruz.
  const isMounted = useRef(true);

  useEffect(() => {
    // Cleanup fonksiyonu: Component unmount olurken çalışır.
    return () => {
      isMounted.current = false;
    };
  }, []); // Boş dependency array: Sadece mount/unmount'ta çalışsın.

  useEffect(() => {
    // Her effect çalıştığında, component'in mount olduğundan emin olalım.
    isMounted.current = true;

    const fetchUser = async () => {
      setLoading(true);
      try {
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();

        // 🛡️ SİHİRLİ KONTROL: Eğer component hala mount durumdaysa state'i güncelle.
        if (isMounted.current) {
          setUser(data);
          setLoading(false);
        }
        // Eğer unmount olmuşsa, bu blok çalışmaz, state güncellenmez, uyarı da olmaz!
      } catch (error) {
        // Hata durumunda da aynı kontrolü yapıyoruz.
        if (isMounted.current) {
          console.error('Veri çekilemedi:', error);
          setLoading(false);
        }
      }
    };

    fetchUser();

    // Bu effect için de cleanup: Eğer userId değişirse, önceki isteğin state güncellemesini engelle.
    return () => {
      isMounted.current = false;
    };
  }, [userId]); // userId değişince yeniden çalış

  if (loading) return <p>Yükleniyor...</p>;
  return <div>{user ? user.name : 'Kullanıcı bulunamadı'}</div>;
}

Bu yöntemdeki anahtar nokta, `useRef` kullanarak component'in mount durumunu takip eden bir değişken (`isMounted`) oluşturmak. `useRef` değeri, component'in tüm render'ları boyunca kalıcıdır ve değişmesi yeniden render tetiklemez. Cleanup fonksiyonu çalıştığında bu değeri `false` yapıyoruz. State'i güncellemeden önce de mutlaka bu değeri kontrol ediyoruz.

💡 Alternatif ve Modern Yaklaşım: AbortController

Eğer sadece uyarıyı susturmak değil de, gereksiz ağ isteklerini de tamamen iptal etmek istiyorsanız, `AbortController` API'si tam size göre. Bu yöntem özellikle büyük dosya indirmeleri veya sık aralıklarla değişen aramalarda çok faydalı.

JavaScript:
useEffect(() => {
  // Yeni bir AbortController örneği oluştur.
  const controller = new AbortController();
  const signal = controller.signal;

  const fetchUser = async () => {
    setLoading(true);
    try {
      // Fetch isteğine abort sinyalini iletiyoruz.
      const response = await fetch(`/api/users/${userId}`, { signal });
      const data = await response.json();
      setUser(data);
      setLoading(false);
    } catch (error) {
      // Eğer hata abort'tan kaynaklandıysa, normal bir hata gibi işleme.
      if (error.name === 'AbortError') {
        console.log('Fetch işlemi iptal edildi:', userId);
      } else {
        console.error('Veri çekilemedi:', error);
        setLoading(false);
      }
    }
  };

  fetchUser();

  // Cleanup fonksiyonunda abort() çağır.
  return () => {
    controller.abort(); // ⏹️ Bu, devam eden fetch isteğini durdurur.
  };
}, [userId]);

Bu yöntem daha agresif ve temiz. Component unmount olur olmaz veya `userId` değişir değişmez, henüz tamamlanmamış olan `fetch` isteği sunucu tarafında bile iptal edilmeye çalışılır (tarayıcı ve sunucu desteğine bağlı olarak). Hem bellek hem de ağ trafiği açısından en verimli çözüm budur.

Sonuç olarak, bu memory leak uyarısı can sıkıcı görünse de, aslında React'ın bizi potansiyel performans ve bellek sorunlarına karşı uyaran güzel bir özelliği. `isMounted` ref'i ile kontrol veya `AbortController` kullanımı, profesyonel uygulamalar yazmak için olmazsa olmaz tekniklerden.

Siz bu sorunla nasıl başa çıkıyorsunuz? `useRef` yöntemini mi tercih edersiniz, yoksa doğrudan `AbortController` ile istekleri iptal etmeyi mi? Ya da React Query, SWR gibi data fetching kütüphaneleri kullanarak bu dertten tamamen kurtulduğunuzu mu düşünüyorsunuz? Yorumlarda deneyimlerinizi paylaşın!
 

Tema özelleştirme sistemi

Bu menüden forum temasının bazı alanlarını kendinize özel olarak düzenleye bilirsiniz.

Zevkine göre renk kombinasyonunu belirle

Tam ekran yada dar ekran

Temanızın gövde büyüklüğünü sevkiniz, ihtiyacınıza göre dar yada geniş olarak kulana bilirsiniz.

Geri