React useEffect Sonsuz Döngü (Infinite Loop) Hatası: 5 Senaryo ve Kesin Çözümler

16.04.2026 23:37 Haber
React useEffect Sonsuz Döngü (Infinite Loop) Hatası: 5 Senaryo ve Kesin Çözümler | Alesta Web

React projenizde sayfanız donuyor, tarayıcı çöküyor ya da console'da binlerce satır log mu görüyorsunuz? Büyük ihtimalle useEffect sonsuz döngüsü (useEffect infinite loop) yaşıyorsunuz. Alesta Web olarak bu hatayı onlarca projede gördük. İyi haber: Her birinin net bir sebebi ve kesin çözümü var. Bu rehberde 5 yaygın senaryoyu kod örnekleriyle açıklayacak, useCallback ve useMemo ile nasıl çözüleceğini göstereceğiz.

useEffect Infinite Loop Neden Olur? (Why Does useEffect Infinite Loop Happen?)

React, useEffect'in çalışıp çalışmayacağına dependency array'i karşılaştırarak karar verir. Sorun şu: dependency değeri her render'da değişirse, effect sürekli çalışır, state günceller, yeni render tetikler ve döngü devam eder.

Döngü Mantığı / Loop Logic

1. Bileşen render edilir
2. useEffect çalışır (dependency değişti!)
3. State güncellenir (setState)
4. Re-render tetiklenir
5. Dependency tekrar değişir
6. useEffect tekrar çalışır → DÖNGÜ!

Temel sebepler: Dependency array'i unutmak, state'i kendi dependency'sine koyup güncellemek, her render'da yeni object/array/function oluşturmak. Alesta Web ekibi olarak bunların hepsini tek tek inceleyeceğiz.

Senaryo 1: Dependency Array Yok (Missing Dependency Array)

En yaygın hata bu. Dependency array'i hiç yazmamak, effect'in her render'da çalışması demektir.

❌ HATALI KOD (Sonsuz Döngü)

function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => setData(data));
    // ⚠️ Dependency array YOK = her render'da çalışır!
  });

  return 
{data}
; }

✅ DOĞRU KOD (Sadece Mount'ta Çalışır)

function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => setData(data));
  }, []); // ✅ Boş array = sadece component mount'ta çalış

  return 
{data}
; }
✅ Kural / Rule:

Effect'in component mount sırasında bir kez çalışmasını istiyorsanız, dependency array'i boş bırakın: []. Bu, "effect sadece ilk render'da çalış" (run only on first render) demektir.

Senaryo 2: State Update Loop (State Güncelleme Döngüsü)

State değişkenini hem dependency'ye koymak hem de effect içinde güncellemek klasik bir tuzak. Alesta Web olarak bunu özellikle sayaç (counter) bileşenlerinde sıkça görüyoruz.

❌ HATALI KOD

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(count + 1); // count'u güncelliyoruz
  }, [count]); // count değişince tekrar çalışıyor = DÖNGÜ!

  return 
{count}
; }

✅ DOĞRU KOD - Fonksiyonel Update

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setTimeout(() => {
      setCount(prevCount => prevCount + 1); // ✅ prevCount kullan
    }, 1000);
    return () => clearTimeout(timer); // cleanup!
  }, []); // count dependency yok, döngü yok!

  return 
{count}
; }
? Alesta Web İpucu:

State güncelleme fonksiyonunda setState(prev => prev + 1) formunu kullandığınızda, önceki değere dependency eklemeden erişebilirsiniz. Bu pattern (fonksiyonel update), sonsuz döngüyü önler.

Senaryo 3: Object Dependency (Obje Referans Sorunu)

JavaScript'te iki objeyi karşılaştırdığınızda, React referans eşitliği (reference equality) kullanır. Her render'da yeni obje oluşturulursa, React "değişti!" der ve effect tekrar çalışır.

❌ HATALI KOD

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

  // Her render'da YENİ obje oluşuyor!
  const config = { userId: userId, timeout: 5000 };

  useEffect(() => {
    fetch(`/api/user/${config.userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [config]); // config referansı her seferinde farklı = DÖNGÜ!

  return 
{user?.name}
; }

✅ DOĞRU KOD - Çözüm 1: useMemo

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

  // useMemo ile referans stabilitesi sağla
  const config = useMemo(() => ({
    userId: userId,
    timeout: 5000
  }), [userId]); // Sadece userId değişince güncelle

  useEffect(() => {
    fetch(`/api/user/${config.userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [config]); // Artık stabil!

  return 
{user?.name}
; }

✅ DOĞRU KOD - Çözüm 2: Primitive Dependency Kullan

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

  useEffect(() => {
    fetch(`/api/user/${userId}`) // userId primitive value
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]); // ✅ number/string = referans sorunu yok

  return 
{user?.name}
; }

Senaryo 4: Function Dependency (Fonksiyon Referans Sorunu)

Fonksiyonlar da her render'da yeni referans oluşturur. Fonksiyonu dependency'e koyduğunuzda, aynı obje sorunu yaşanır.

❌ HATALI KOD

function DataProcessor() {
  const [data, setData] = useState(null);

  // Her render'da YENİ fonksiyon oluşuyor!
  const fetchData = () => {
    return fetch('/api/data').then(r => r.json());
  };

  useEffect(() => {
    fetchData().then(setData);
  }, [fetchData]); // Fonksiyon referansı değişiyor = DÖNGÜ!

  return 
{data}
; }

✅ DOĞRU KOD - Çözüm 1: useCallback

function DataProcessor() {
  const [data, setData] = useState(null);

  // useCallback ile fonksiyon referansı sabitlendi
  const fetchData = useCallback(() => {
    return fetch('/api/data').then(r => r.json());
  }, []); // Dependency yoksa referans asla değişmez

  useEffect(() => {
    fetchData().then(setData);
  }, [fetchData]); // ✅ fetchData artık stabil

  return 
{data}
; }

✅ DOĞRU KOD - Çözüm 2: Fonksiyonu Effect İçine Taşı

function DataProcessor() {
  const [data, setData] = useState(null);

  useEffect(() => {
    // Fonksiyonu doğrudan effect içinde tanımla
    const fetchData = async () => {
      const response = await fetch('/api/data');
      return response.json();
    };

    fetchData().then(setData);
  }, []); // Fonksiyon dependency'de yok = sorun yok

  return 
{data}
; }

Senaryo 5: Array Dependency (Dizi Referans Sorunu)

Diziler de her render'da yeni referans oluşturur. Obje sorunuyla aynı mantık geçerli.

❌ HATALI KOD

function ListRenderer() {
  const [items, setItems] = useState([]);

  const filterList = [1, 2, 3]; // Her render'da yeni dizi!

  useEffect(() => {
    const filtered = items.filter(item => filterList.includes(item));
    console.log('Filtered:', filtered);
  }, [filterList]); // Dizi referansı değişiyor = DÖNGÜ!

  return 
{items.length}
; }

✅ DOĞRU KOD - useMemo ile

function ListRenderer() {
  const [items, setItems] = useState([]);

  // useMemo ile dizi referansını sabitle
  const filterList = useMemo(() => [1, 2, 3], []);

  useEffect(() => {
    const filtered = items.filter(item => filterList.includes(item));
    console.log('Filtered:', filtered);
  }, [filterList, items]); // ✅ Stabil referans

  return 
{items.length}
; }

ESLint exhaustive-deps Kuralı (ESLint Rule)

Alesta Web olarak kesinlikle tavsiye ediyoruz: react-hooks/exhaustive-deps ESLint kuralını aktif edin. Bu kural, dependency array'de eksik değişkenleri otomatik tespit eder ve stale closure hatalarını önler.

.eslintrc.json Yapılandırması

{
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  }
}

ESLint'in Yakaladığı Örnek

// ⚠️ ESLint Warning: userId'yi dependency'ye ekle
function Analytics({ userId }) {
  useEffect(() => {
    trackEvent('page_view', userId);
  }, []); // ESLint: 'userId' is missing in dependency array
}

// ✅ DOĞRU
function Analytics({ userId }) {
  useEffect(() => {
    trackEvent('page_view', userId);
  }, [userId]); // Doğru!
}

React 18 ve 19'da Değişiklikler (React 18 & 19 Changes)

? React 18 Strict Mode Davranışı:

React 18'de Strict Mode, development ortamında useEffect'i iki kez çalıştırır (effect → cleanup → effect). Bu bir hata değil, kasıtlı bir davranış. Infinite loop varsa bunu çok daha hızlı fark etmenizi sağlar. Production'da normal şekilde çalışır.

React 18 - Fonksiyonel Update Önerisi

// ⚠️ React 18'de bazen eski değer kullanılabiliyor
function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(count + 1); // Eski değer riski!
  }, [count]);
}

// ✅ DOĞRU - Fonksiyonel update her zaman güvenli
function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(prevCount => prevCount + 1); // Güvenli!
  }, []);
}

Özet Çizelgesi / Quick Reference

Sorun / Problem Neden / Why Çözüm / Solution
Dependency array yok Her render'da çalışır [] ekle
State + dependency loop setState → re-render → tekrar Fonksiyonel update
Object dependency Her render'da yeni referans useMemo veya primitive
Function dependency Her render'da yeni referans useCallback veya effect içine taşı
Array dependency Her render'da yeni referans useMemo veya useRef

? Kaynaklar ve Referanslar / Sources and References

Alesta Web olarak tüm kod örneklerini React 18/19 ile test ettik (all code examples tested with React 18/19).

✅ useEffect Sonsuz Döngüsü Çözüldü! (Infinite Loop Fixed!)

React useEffect infinite loop hatasının 5 yaygın sebebini ve çözümlerini gördünüz. Alesta Web ekibi olarak şunu söyleyelim: Bu hataların hepsi dependency array'ini doğru yönetmekle çözülüyor. ESLint exhaustive-deps kuralını aktif etmek ise bu hataları kod yazarken yakalatır.

Hızlı Özet / Quick Summary:

  • ✅ Boş [] = sadece mount'ta çalış
  • ✅ Object/array dependency için useMemo kullan
  • ✅ Function dependency için useCallback kullan
  • ✅ State güncellemede fonksiyonel update tercih et
  • ✅ ESLint exhaustive-deps rule'u aktif et (enable exhaustive-deps ESLint rule)

Faydalı Linkler / Useful Links:

© 2026 AlestaWeb - Tüm hakları saklıdır.

WM Tools
💫

WebMaster Tools

15 Profesyonel Araç
Alesta AI
Alesta AI
Online