Cansu Arı Logo
Blog
Nasıl Yapılır?

Custom Hook Yazarken Side Effect Yönetimi

Custom Hook yazmak React’te kod tekrarını azaltır ama useEffect yönetimi doğru yapılmazsa kaos başlar.

  • #React

Skip to content

Custom Hook Yazmayalım mı?

React’te “Custom Hook” yazmak, tekrar eden kodları soyutlamanın en güçlü yolu.
Ama yanlış yerde useEffect kullanmak, seni kolayca sonsuz döngüler, memory leak’ler veya veri tutarsızlıklarıyla baş başa bırakabilir. 😊

Bu yazıda, Custom Hook’larda side effect’lerin (yan etkilerin) nasıl yönetilmesi gerektiğini, en yaygın hataları ve doğru pattern’leri inceleyeceğiz.


Hatırlatma: Side Effect Nedir?

“Side effect”, component’in render’ından bağımsız çalışan işlemdir. Yani React’in saf fonksiyon mantığını bozan her şey:
  • Veri çekmek (fetch)
  • Event listener eklemek
  • DOM veya storage manipülasyonu
  • Timer veya subscription başlatmak
Kısaca: “Render dışında gerçekleşen her şey” bir side effect’tir.

React’te bu işleri kontrol altına almak için useEffect, useLayoutEffect gibi hook’lar kullanılır.
Custom Hook yazarken bu hook’lar, dış dünyayla etkileşim kurmanın ana aracıdır.


Basit Bir Custom Hook Örneği Verelim;

function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth)

useEffect(() => {
const handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)

return () => window.removeEventListener('resize', handleResize)
}, [])

return width
}
Bu hook pencere genişliğini izler.
  • Side effect: addEventListener → dış dünya ile etkileşim.
  • Cleanup: removeEventListener → sızıntıyı önler.
Bu yapıyı doğru kurmazsan, her render’da yeni listener eklenir ve performans hızla çöker. 💥

Yaygın Hata 1: Dependency Kaosu

Custom Hook içinde useEffect kullanıyorsan, dependency listesi hayati önem taşır.
useEffect(() => {
fetch(`/api/data?query=${query}`)
}, []) // ❌ query dependency’si eksik!
Bu durumda query değişse bile, fetch sadece bir kez çalışır.

Ama eklersen:

useEffect(() => {
fetch(`/api/data?query=${query}`)
}, [query])

Artık her değişiklikte fetch yapılır.

💡 İpucu: ESLint’in react-hooks/exhaustive-deps kuralını asla kapatma. Bu kural, hayat kurtarır.


Yaygın Hata 2: Cleanup Eksikliği

Bir Custom Hook timer, subscription veya event listener başlatıyorsa, mutlaka cleanup dönmelidir.
function useTimer() {
useEffect(() => {
const id = setInterval(() => console.log('tick'), 1000)
return () => clearInterval(id)
}, [])
}
Eğer clearInterval eklemezsen, component unmount olsa bile işlem devam eder. Sonuç: Hafıza sızıntısı (memory leak).💥

Yaygın Hata 3: State ve Effect’in Ayrılmaması

Bazı geliştiriciler tek useEffect içinde hem state hem side effect yönetir -- bu okunabilirliği bozar.
useEffect(() => {
setData(fetchData()) // ❌ Hem state, hem effect
}, [])
Doğrusu:
useEffect(() => {
fetchData().then(setData)
}, [])
Ya da daha temiz: Ayrı hook’lar.
const data = useDataFetcher('/api/posts')
Bu yapı test edilebilirliği ve yeniden kullanılabilirliği artırır.

Custom Hook’larda useEffect Kullanım Desenleri

1. Veri Çekme (Data Fetching)

function useFetch(url) {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)

useEffect(() => {
let ignore = false
setLoading(true)
fetch(url)
.then(res => res.json())
.then(json => { if (!ignore) setData(json) })
.catch(setError)
.finally(() => setLoading(false))
return () => { ignore = true }
}, [url])

return { data, error, loading }
}
Bu pattern’de “ignore flag” ile unmount sonrası setState engellenir -- klasik React hatası: "Can't perform a React state update on an unmounted component." önlenir. ✅

2. Event Listener Yönetimi

function useKeyPress(targetKey) {
const [pressed, setPressed] = useState(false)

useEffect(() => {
const down = (e) => e.key === targetKey && setPressed(true)
const up = (e) => e.key === targetKey && setPressed(false)

window.addEventListener('keydown', down)
window.addEventListener('keyup', up)
return () => {
window.removeEventListener('keydown', down)
window.removeEventListener('keyup', up)
}
}, [targetKey])

return pressed
}
Reaktif, güvenli ve temiz -- Hook dünyasında küçük bir sanat eseri. ✅

Side Effect Yönetiminde En İyi Uygulamalar

  1. Cleanup’ı asla unutma. Her effect bir iz bırakır, sen silmezsen React silmez.
  2. Dependency dizisini doğru doldur. Her dependency, deterministik davranış sağlar.
  3. Async işlemleri iptal et. Fetch işlemlerinde AbortController kullanmak modern bir çözümdür.
  4. Effect’leri böl. Bir useEffect birden fazla işi yapmamalı.
  5. Custom Hook test edilebilir olmalı. DOM’a dokunmadan jest/react-testing-library ile kontrol edilebilmeli.

Reaktif Pattern: Effect + Memo Kombosu

function useFilteredList(list, query) {
const filtered = useMemo(() => list.filter(i => i.includes(query)), [list, query])
useEffect(() => {
console.log('Liste güncellendi')
}, [filtered])
return filtered
}
Burada useMemo hesaplamayı optimize eder, useEffect yalnızca sonuç değişince çalışır. Performans + doğruluk el ele. 🤝

Debugging İpuçları

  • React DevTools → Components sekmesinde Hook değerlerini izle.
  • useEffect tetiklenme sayısını logla.
  • Gereksiz render’ı tespit etmek için React Profiler kullan.
useEffect(() => {
console.count('Effect tetiklendi')
})
Bir sayfa 5 kez render oluyorsa, nedenini hemen anlarsın.

Sonuç

Custom Hook’lar kodunuzu sadeleştirir ama useEffect yönetimi doğru yapılmazsa karmaşayı artırır.

Kısaca:

  • Side effect = render dışı işlem.
  • Her effect’in bir cleanup’ı olmalı.
  • Dependencies hayat kurtarır.
  • Custom Hook’lar test edilebilir ve izole olmalı.

All tags
  • React