useEffect — Bağımlılıklar ve Temizlik
Önce şunu oku: React Render Modeli
30 saniyede özet
useEffect'te iki ayrı hata yapılabilir: eksik bağımlılık eski veriyi gösterir, eksik temizlik açık bağlantı bırakır. Çoğu kişi birini fark edip ötekini düzeltir.
Konsoldaki bir uyarıyı susturmak için küçük bir değişiklik yaptın ve sorun çözüldü sandın. Uyarı sustu — ama haklıydı.
O değişiklik, effect’in bağımlılık dizisini boş bırakmaktı: useEffect(..., []).
-
Bayt: Effect'in dizisini boş bıraktım, uyarı sustu. Bir kez çalışıyor, tertemiz!
-
Sen: Kullanıcı değişince ne oluyor peki?
-
Bayt: Ekranda yeni kullanıcı var ama gelen mesajlar hâlâ eskisinin...
-
Bayt: Cebimdeki fotoğraf eski kalmış olabilir mi?
Adım adım oku
- Effect ilk render'da çalışır ve o anki değerleri, burada userId 1'i, yanına alır.
- Dizi boşsa userId 2 olsa bile effect yeniden çalışmaz; cebindeki fotoğraf eski kalır.
- Effect yeniden çalışsa ama temizlik yazılmamış olsa, eski abonelik kapanmaz ve ikisi birden açık kalır.
- Dizide userId ve bir temizlik varsa sıra hep aynıdır: önce eski abonelik kapanır, sonra yenisi açılır.
Bağımlılık dizisi ne yapar?
React her render’dan sonra diziyi bir öncekiyle karşılaştırır. Hepsi aynıysa effect’i atlar.
Karşılaştırma Object.is ile yapılır — yani referansa bakar, içeriğe değil. Bu tek cümle, aşağıdaki hataların çoğunu açıklar.
| Yazım | Ne zaman çalışır |
|---|---|
useEffect(fn) | Her render’dan sonra |
useEffect(fn, []) | Yalnızca mount’ta |
useEffect(fn, [userId]) | Mount’ta ve userId değiştiğinde |
Dizi yazmamak ile boş dizi yazmak taban tabana zıttır. İkisi de nadiren doğru cevaptır.
Kafam karıştı, daha basit anlat
Bağımlılık dizisi bir kontrol listesidir. React her çizimden sonra listeye bakar: bir şey değiştiyse effect’i yeniden çalıştırır, hiçbir şey değişmediyse atlar.
Kendin gör
useEffect — bağımlılık dizisi, temizlik ve StrictMode
Tohum 2useEffect(() => {
const sub = chat.subscribe(userId);
}, []);Yaşam döngüsü — adım 0/5
Effect / temizlik
0 / 0
dengeli
Açık abonelik
0
temiz
Abone olunan userId
—/ güncel 1
güncel · 0 atlanan
Şu an ne oldu?
Bileşen henüz mount edilmedi
Effect render sırasında değil, render ekrana yansıdıktan sonra çalışır. Bu yüzden ilk boyama hiçbir zaman effect’i beklemez.
Aklında kalsın: useEffect render’ın parçası değildir; React ekranı güncelledikten sonra çalışan bir yan etkidir.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
Sırayla dene:
[]+ temizlikEffect'in döndürdüğü fonksiyon. Unmount'ta **ve** effect her yeniden çalışmadan önce koşar; ikincisi en çok atlanan kısımdır.Sözlükte gör → yok. Sonuna kadar oynat. İki hata birden:userId2 olduğu hâlde abonelik hâlâ 1’de (bayat), ve unmount’ta bağlantı açık kaldı (sızıntı).- Temizliği aç, dizi
[]kalsın. Sızıntı bitti — ama bayatlık duruyor. Bunlar farklı hatalar. - Diziyi
[userId]yap. İkisi de bitti. Effect 2 kez çalıştı, temizlik 2 kez — sayılar eşit. [options]’a geç. Effect her render’da çalışıyor, çünkü nesne her render’da yeniden oluşuyor.- Temizliği kapat, StrictMode’u aç. Sızıntı ilk render’da görünür oluyor.
`useEffect`'in bağımlılık dizisi boş olursa (`[]`) ne olur?
İki farklı hata
Bayat kapanış — eksik bağımlılık
Effect gövdesi, çalıştığı andaki değerleri kapatır (closureBir fonksiyonun, tanımlandığı andaki değişkenleri yanında taşıması. Effect yeniden çalışmazsa eski değerleri taşımaya devam eder — bayat closure hatası budur.Sözlükte gör →). Yeniden çalışmazsa o eski değerleri kullanmaya devam eder.
useEffect(() => { const sub = chat.subscribe(userId); // mount'taki userId return () => sub.close();}, []); // userId değişse bile çalışmazBelirti: ekranda yanlış kullanıcının verisi, tıklamanın hiçbir şey yapmaması, sayaç hep aynı sayıyı göstermesi.
Sızıntı — eksik temizlik
useEffect(() => { const sub = chat.subscribe(userId); // return yok}, [userId]);Bağımlılıklar doğru, effect doğru zamanlarda çalışıyor — ama her çalışma yeni bir abonelik açıyor ve eskisi kapanmıyor.
Belirti: zamanla artan bellek, kapanmış bir bileşene gelen güncellemeler, aynı isteğin birden çok kez gönderilmesi.
Kafam karıştı, daha basit anlat
Listeye bir değeri yazmayı unutursan, effect o değerin eski fotoğrafıyla çalışmaya devam eder. Değer değişir, effect bundan haberdar olmaz.
Bu bileşende butona bir kez basıldıktan 1 saniye sonra ne yazdırılır?
Temizlik ne zaman çalışır?
Effect userId için bir abonelik açıyor ve temizliğinde kapatıyor. userId 1'den 2'ye değişti. Bileşen ekranda kalıyor. Temizlik çalışır mı? Cevabı göster
Evet, hem de yeni effect’ten önce. Önce eski abonelik kapanır, sonra yenisi açılır.
Yaygın yanılgı: “temizlik unmount’ta çalışır.” Eksik.
Temizlik her yeniden çalışmadan önce de çalışır. Sıra her zaman aynıdır:
userId 1 → 2 değişti 1. eski effect'in temizliği sub.close() ← önce bu 2. yeni effect subscribe(2) ← sonra buBu sıra, iki aboneliğin aynı anda açık olmasını engeller. Bilmek bağımlılık hatalarının yarısını önler.
Bir bankanın döviz kuru ekranında bu sırayı canlı görebilirsin. Kullanıcı dolardan euroya geçince neyin kapanıp neyin açıldığına birlikte bakalım.
Derinleş · Canlı döviz kuru: çift değişince akış da değişir 4 dosya · ~77 satır · ilk okumada atlayabilirsin
Kafam karıştı, daha basit anlat
Temizlik yalnızca en sonda değil, effect her yeniden çalışmadan önce de çalışır. Önce eskisini toplar, sonra yenisini kurar.
Effect'in döndürdüğü temizlik fonksiyonu ne zaman çalışır?
Her belirtiyi hangi hatanın ürettiğine göre sınıflandır.
Tuzaklar: her seferinde yeni nesne
Bu kod, bağımlılık dizisi dolu olmasına rağmen her render’da çalışır:
const options = { id: userId }; // her render'da yeni nesne
useEffect(() => { const sub = chat.subscribe(options);}, [options]); // referans her zaman farklıAynı sorun fonksiyonlarda ve dizilerde de var. Üç çözüm:
- Primitifi doğrudan koy.
[userId]— en basit ve en iyi çözüm. useMemo/useCallbackile referansı sabitle — gerçekten nesne gerekiyorsa.- Nesneyi effect’in içine taşı — dışarıda tutulmasına gerek yoksa.
İlk seçenek neredeyse her zaman doğrudur. Diğer ikisi, birinci mümkün değilse gelir.
StrictMode neden iki kez çalıştırıyor. Geliştirmede React bileşeni mount eder, hemen kaldırır ve tekrar mount eder. Bu bir hata değil, bir testtir.
Temizlik doğru yazılmışsa hiçbir şey değişmez: bir abonelik açılır, kapanır, bir tane daha açılır — sonuçta yine tek abonelik. Temizlik yoksa iki abonelik açık kalır ve sorun ilk render’da görünür.
Bu davranış üretim derlemesinde yoktur. “StrictMode’u kapatalım” demek, testi kapatıp hatayı üretime bırakmaktır.
Bir değeri bağımlılık dizisinden çıkarmak (lint uyarısını bastırmak) neden tehlikeli?
Fetch için doğru kalıp. En sık karşılaşılan durum: bir effect içinde veri çekmek ve yarış durumundan (race conditionSonucun thread'lerin hangi sırayla çalıştığına bağlı olması. Kod çoğu zaman doğru çalışır, sonra bir gün çalışmaz.Sözlükte gör →) kaçınmak.
useEffect(() => { const controller = new AbortController();
fetch(`/api/users/${userId}`, { signal: controller.signal }) .then((res) => res.json()) .then(setUser) .catch((err) => { if (err.name !== 'AbortError') setError(err); });
return () => controller.abort();}, [userId]);Temizlik olmadan şu olur: userId hızlıca 1’den 2’ye değişir, iki istek uçar, 1’in cevabı sonra gelirse ekranda yanlış kullanıcı kalır. abort() bunu kapatır.
Pratikte bu işi React Query gibi bir kütüphaneye devretmek daha iyidir — ama neyi devrettiğini bilmeden devretmek, hata çıktığında seni çaresiz bırakır.
Kendini sına
Boş bağımlılık dizisi, effect'in ilk render'daki değerleri kullanmaya devam etmesine yol açabilir.
Bu bileşende butona üç kez basıldıktan 3 saniye sonra konsolda ne görünür?
Aklında kalacak üç şey
- 1 Boş dizi 'bir kez çalış' demek değil, 'ilk render'ın değerlerini sonsuza kadar kullan' demektir. Eski değerin takılı kalması buradan gelir.
- 2 Temizlik yalnızca bileşen kaldırılırken çalışmaz, her yeniden çalışmadan önce de çalışır. Sıra hep aynıdır: önce eski temizlik, sonra yeni effect.
- 3 İki ayrı hata var: eksik bağımlılık eski veri gösterir, eksik temizlik bağlantı sızdırır. Belirtileri de çözümleri de farklıdır.
4 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.