Merhaba arkadaşlar, bugün React'te hepimizin başını ağrıtan, özellikle de `useEffect` içinde sonsuz döngülere veya gereksiz yeniden render'lara yol açan bir konudan bahsedeceğim: dependency array'ine koyduğumuz fonksiyonların stabilitesi. Bu hatayı ilk gördüğümde, "Ben sadece bir fonksiyon çağırıyorum, neden sürekli tetikleniyor?" diye kafayı yemiştim. İşte bu sorunu `useCallback` ile nasıl çözdüğümü anlatacağım.
Sorun Ne? Neden Referans Önemli?
React'te bileşenler her render olduğunda, içinde tanımlanan fonksiyonlar da yeniden oluşturulur. Bu, her seferinde farklı bir bellek adresi (referans) anlamına gelir. Şöyle bir senaryo düşünün:
Yukarıdaki kodda, butona her tıkladığımızda `count` state'i değişir, bu da bileşeni yeniden render eder. Yeniden render, `fetchData` fonksiyonunun yeni bir versiyonunu yaratır. `useEffect` ise dependency array'inde `[fetchData]`'yı gördüğü ve bu her seferinde değiştiği için her render'da çalışır. Bu, istenmeyen sonsuz döngülere veya çok fazla API isteğine yol açabilir.
Çözüm: useCallback ile Referansı Sabitlemek
İşte tam burada `useCallback` kurtarıcımız oluyor. `useCallback`, bir fonksiyonu memoize eder (hatırlar ve saklar). Dependency array'ine bağlı olarak, fonksiyonun referansını sadece belirttiğimiz bağımlılıklar değiştiğinde yeniler. Aynı `useEffect` gibi çalışır ama fonksiyonlar için.
Yukarıdaki sorunlu kodu `useCallback` ile düzeltelim:
Şimdi ne oldu? `fetchData` fonksiyonunun referansı, `count` değişmediği sürece aynı kalacak. Bu sayede `useEffect`'in dependency array'ine koyduğumuzda, bileşen gereksiz yere render olsa bile (başka bir state değişse bile) `fetchData` değişmeyeceği için `useEffect` tekrar çalışmayacak. Optimizasyon sağlandı!
Ne Zaman Kullanmalı, Ne Zaman Kullanmamalı?
`useCallback` sihirli bir değnek değil. Her fonksiyonu sarmalamanız gerekmez. Performans maliyeti vardır. İşte benim kullandığım pratik kurallar:
1. Fonksiyonunuz bir `useEffect` dependency array'inde kullanılıyorsa, düşünün.
2. Alt bileşenlere prop olarak geçirilen ve React.memo ile sarmalanmış bileşenlerde gereksiz render'ı önlemek için kullanın.
3. Fonksiyonunuz çok basitse ve bağımlılığı yoksa, kullanmanıza gerek yok. Örneğin: `const sayHello = () => console.log('Merhaba');`
Son Söz ve Sorum
`useCallback`, `useEffect` ile birlikte kullanıldığında gerçek gücünü gösteriyor. Referans karşılaştırmasının React dünyasında ne kadar kritik olduğunu anlamak, bu tür hatalardan kurtulmanın ilk adımı. Ben bu yöntemi öğrendikten sonra "useEffect infinite loop" hatalarım büyük ölçüde azaldı.
Peki ya siz? `useCallback` kullanırken başka hangi senaryolarda faydasını gördünüz? Ya da belki "Ben `useRef` ile de bu sorunu çözüyorum" diyenler vardır aranızda? Yorumlarda deneyimlerinizi paylaşın, tartışalım!
React'te bileşenler her render olduğunda, içinde tanımlanan fonksiyonlar da yeniden oluşturulur. Bu, her seferinde farklı bir bellek adresi (referans) anlamına gelir. Şöyle bir senaryo düşünün:
JavaScript:
function MyComponent() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
// Her render'da YENİ bir `fetchData` fonksiyonu oluşur!
const fetchData = () => {
console.log('Fetching data for count:', count);
// API isteği simülasyonu...
setData(`Data for ${count}`);
};
useEffect(() => {
fetchData();
}, [fetchData]); // `fetchData` her render'da değiştiği için useEffect her seferinde tetiklenir!
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Arttır</button>
</div>
);
}
Yukarıdaki kodda, butona her tıkladığımızda `count` state'i değişir, bu da bileşeni yeniden render eder. Yeniden render, `fetchData` fonksiyonunun yeni bir versiyonunu yaratır. `useEffect` ise dependency array'inde `[fetchData]`'yı gördüğü ve bu her seferinde değiştiği için her render'da çalışır. Bu, istenmeyen sonsuz döngülere veya çok fazla API isteğine yol açabilir.
İşte tam burada `useCallback` kurtarıcımız oluyor. `useCallback`, bir fonksiyonu memoize eder (hatırlar ve saklar). Dependency array'ine bağlı olarak, fonksiyonun referansını sadece belirttiğimiz bağımlılıklar değiştiğinde yeniler. Aynı `useEffect` gibi çalışır ama fonksiyonlar için.
Yukarıdaki sorunlu kodu `useCallback` ile düzeltelim:
JavaScript:
function MyComponent() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
// `useCallback` ile fonksiyonu memoize ediyoruz.
// Referans, sadece `count` değiştiğinde yenilenecek.
const fetchData = useCallback(() => {
console.log('Fetching data for count:', count);
setData(`Data for ${count}`);
}, [count]); // Bağımlılık: `count`
useEffect(() => {
fetchData();
}, [fetchData]); // Artık `fetchData` sadece `count` değiştiğinde değişeceği için, useEffect de sadece o zaman tetiklenecek.
return (
<div>
<p>Count: {count}</p>
<p>Data: {data}</p>
<button onClick={() => setCount(c => c + 1)}>Arttır ve Data Getir</button>
</div>
);
}
Şimdi ne oldu? `fetchData` fonksiyonunun referansı, `count` değişmediği sürece aynı kalacak. Bu sayede `useEffect`'in dependency array'ine koyduğumuzda, bileşen gereksiz yere render olsa bile (başka bir state değişse bile) `fetchData` değişmeyeceği için `useEffect` tekrar çalışmayacak. Optimizasyon sağlandı!
`useCallback` sihirli bir değnek değil. Her fonksiyonu sarmalamanız gerekmez. Performans maliyeti vardır. İşte benim kullandığım pratik kurallar:
1. Fonksiyonunuz bir `useEffect` dependency array'inde kullanılıyorsa, düşünün.
2. Alt bileşenlere prop olarak geçirilen ve React.memo ile sarmalanmış bileşenlerde gereksiz render'ı önlemek için kullanın.
3. Fonksiyonunuz çok basitse ve bağımlılığı yoksa, kullanmanıza gerek yok. Örneğin: `const sayHello = () => console.log('Merhaba');`
`useCallback`, `useEffect` ile birlikte kullanıldığında gerçek gücünü gösteriyor. Referans karşılaştırmasının React dünyasında ne kadar kritik olduğunu anlamak, bu tür hatalardan kurtulmanın ilk adımı. Ben bu yöntemi öğrendikten sonra "useEffect infinite loop" hatalarım büyük ölçüde azaldı.
Peki ya siz? `useCallback` kullanırken başka hangi senaryolarda faydasını gördünüz? Ya da belki "Ben `useRef` ile de bu sorunu çözüyorum" diyenler vardır aranızda? Yorumlarda deneyimlerinizi paylaşın, tartışalım!