Merhaba arkadaşlar, bugün React'te performans optimizasyonu yaparken hepimizin kafasını karıştıran bir konudan bahsedeceğim: useCallback. Özellikle useEffect veya useMemo gibi hook'ların dependency array'lerine bir event handler fonksiyonu yazdığımızda, o fonksiyonun her render'da yeniden oluşması bizi sürekli re-render döngüsüne sokabiliyor. "Bu hatayı ilk gördüğümde kafayı yemiştim" diyebilirim. Peki, her fonksiyonu useCallback ile sarmalamak doğru mu? Kesinlikle hayır! İşte benim projelerimde uyguladığım, işe yarayan kriterler.
Neden Her Şeyi useCallback'e Almıyoruz?
useCallback, aslında bir fonksiyonun referansını (bellekteki adresini) korumak için kullanılır. Ancak, bu korumanın da bir maliyeti var. Gereksiz yere kullanıldığında, React'in kendi iç optimizasyonlarını bile bozabilir ve kodunuzu daha karmaşık hale getirebilir. Temel kuralım şu: Eğer bir fonksiyon, bir child component'e prop olarak geçiriliyorsa ve o child, React.memo ile sarmalanmışsa, o zaman useCallback ciddi bir değer katabilir. Aksi takdirde, çoğu zaman premature optimization (erken optimizasyon) olur.
Kullanım Kriterlerim ve Kod Örnekleri
İşte benim takip ettiğim pratik kurallar:
1. Child Component'e Prop Geçirme Kuralı: Fonksiyonunuz, memoize edilmiş (React.memo) bir child component'e prop olarak geçiriliyorsa, useCallback kullanın. Yoksa, child her seferinde gereksiz yere re-render olur.
2. Dependency Array Zorunluluğu Kuralı: Fonksiyonunuz, bir useEffect, useMemo veya başka bir useCallback'in dependency array'inde kullanılıyorsa, ve fonksiyonun içinde state veya prop değerleri kullanıyorsa, useCallback kullanın ve bağımlılıkları doğru belirtin.
3. Gereksiz Kullanımdan Kaçınma Kuralı: Eğer fonksiyon sadece local bir event handler ise ve yukarıdaki iki durumdan hiçbirine girmiyorsa, useCallback kullanmayın. Basit tutun.
Sonuç ve Tavsiyelerim
useCallback bir silah değil, bir araçtır. Amacı, referans eşitliğinin önemli olduğu durumlarda (memoization, dependency array'ler) performansı artırmaktır. Projenizde her yerde kullanmaya başlamadan önce, yukarıdaki iki ana kriteri kontrol edin. Profiler tool'unu kullanarak gereksiz re-render'ları tespit edin ve optimizasyonu ona göre yapın.
Siz React projelerinizde useCallback'i hangi durumlarda kullanıyorsunuz? Hiç gereksiz kullanımın performansı düşürdüğü bir senaryo ile karşılaştınız mı? Yorumlarda deneyimlerinizi paylaşın, beraber öğrenelim!
useCallback, aslında bir fonksiyonun referansını (bellekteki adresini) korumak için kullanılır. Ancak, bu korumanın da bir maliyeti var. Gereksiz yere kullanıldığında, React'in kendi iç optimizasyonlarını bile bozabilir ve kodunuzu daha karmaşık hale getirebilir. Temel kuralım şu: Eğer bir fonksiyon, bir child component'e prop olarak geçiriliyorsa ve o child, React.memo ile sarmalanmışsa, o zaman useCallback ciddi bir değer katabilir. Aksi takdirde, çoğu zaman premature optimization (erken optimizasyon) olur.
İşte benim takip ettiğim pratik kurallar:
1. Child Component'e Prop Geçirme Kuralı: Fonksiyonunuz, memoize edilmiş (React.memo) bir child component'e prop olarak geçiriliyorsa, useCallback kullanın. Yoksa, child her seferinde gereksiz yere re-render olur.
JavaScript:
// Child Component (Memoize Edilmiş)
const MemoizedButton = React.memo(({ onClick, label }) => {
console.log('Button rendered:', label);
return <button onClick={onClick}>{label}</button>;
});
// Parent Component
function ParentComponent() {
const [count, setCount] = useState(0);
// BU FONKSİYON useCallback İLE SARMALANMALI
const handleIncrement = useCallback(() => {
setCount(prev => prev + 1);
}, []); // Dependency array boş, çünkü setCount stabil
return (
<div>
<p>Count: {count}</p>
{/ Aşağıdaki onClick prop'u stabil kalır, buton gereksiz render olmaz /}
<MemoizedButton onClick={handleIncrement} label="Arttır" />
</div>
);
}
2. Dependency Array Zorunluluğu Kuralı: Fonksiyonunuz, bir useEffect, useMemo veya başka bir useCallback'in dependency array'inde kullanılıyorsa, ve fonksiyonun içinde state veya prop değerleri kullanıyorsa, useCallback kullanın ve bağımlılıkları doğru belirtin.
JavaScript:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
// BU FONKSİYON useCallback İLE SARMALANMALI
// Çünkü `fetchUserData` useEffect'in dependency array'inde.
const fetchUserData = useCallback(async () => {
const data = await fetch(`/api/user/${userId}`);
const json = await data.json();
setUser(json);
}, [userId]); // userId değiştiğinde fonksiyon yeniden oluşturulur
useEffect(() => {
fetchUserData();
}, [fetchUserData]); // fetchUserData stabil kalana kadar bu efekt tekrar çalışmaz
return <div>{/ Kullanıcı bilgileri /}</div>;
}
3. Gereksiz Kullanımdan Kaçınma Kuralı: Eğer fonksiyon sadece local bir event handler ise ve yukarıdaki iki durumdan hiçbirine girmiyorsa, useCallback kullanmayın. Basit tutun.
JavaScript:
function SimpleForm() {
const [input, setInput] = useState('');
// BU FONKSİYON İÇİN useCallback GEREKSİZ
// Sadece local state'i günceller, child'a geçirilmiyor veya başka bir hook'ta kullanılmıyor.
const handleInputChange = (event) => {
setInput(event.target.value);
};
return <input type="text" value={input} onChange={handleInputChange} />;
}
useCallback bir silah değil, bir araçtır. Amacı, referans eşitliğinin önemli olduğu durumlarda (memoization, dependency array'ler) performansı artırmaktır. Projenizde her yerde kullanmaya başlamadan önce, yukarıdaki iki ana kriteri kontrol edin. Profiler tool'unu kullanarak gereksiz re-render'ları tespit edin ve optimizasyonu ona göre yapın.
Siz React projelerinizde useCallback'i hangi durumlarda kullanıyorsunuz? Hiç gereksiz kullanımın performansı düşürdüğü bir senaryo ile karşılaştınız mı? Yorumlarda deneyimlerinizi paylaşın, beraber öğrenelim!