Ulaşım
- Adres:2342 Sk, İpekyol, İpek Ap 49A, 63250 Haliliye/Şanlıurfa
- Telefon:
0542 315 45 37 - eMail: info@alestaweb.com
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.
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.
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.
En yaygın hata bu. Dependency array'i hiç yazmamak, effect'in her render'da çalışması demektir.
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};
}
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};
}
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.
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.
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};
}
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};
}
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.
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.
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};
}
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};
}
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};
}
Fonksiyonlar da her render'da yeni referans oluşturur. Fonksiyonu dependency'e koyduğunuzda, aynı obje sorunu yaşanır.
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};
}
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};
}
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};
}
Diziler de her render'da yeni referans oluşturur. Obje sorunuyla aynı mantık geçerli.
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};
}
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};
}
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.
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
// ⚠️ 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'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'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!
}, []);
}
| 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 |
Alesta Web olarak tüm kod örneklerini React 18/19 ile test ettik (all code examples tested with React 18/19).
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:
[] = sadece mount'ta çalışuseMemo kullanuseCallback kullanFaydalı Linkler / Useful Links:
© 2026 AlestaWeb - Tüm hakları saklıdır.