- Konu sahibi
- #1
useMemo, bir hesaplamanın ürettiği değeri bağımlılıkları değişmediği sürece yeniden kullanır. useCallback ise bir fonksiyonun referansını bağımlılıkları değişmediği sürece korur. Yani useMemo değer, useCallback fonksiyon önbellekler.İkisi de performans optimizasyonudur; uygulamanın doğru çalışması için her yerde kullanılmaları gerekmez. Özellikle pahalı hesaplamaları veya
React.memo ile sarılmış alt bileşenlere aktarılan fonksiyonların gereksiz değişmesini önlemek için kullanılır.Nasıl çalışırlar?
React bir bileşeni yeniden render ettiğinde gövdesindeki ifadeler yeniden çalışır. Normal bir fonksiyon tanımı da her render işleminde yeni bir fonksiyon referansı oluşturur. Nesne ve dizi ifadeleri de yeni referanslar üretir.
useMemo(() => hesapla(), [bagimliliklar]) hesaplama sonucunu saklar. Listedeki bağımlılıklardan biri değişirse hesaplama tekrar yapılır.useCallback(() => islem(), [bagimliliklar]) fonksiyon referansını saklar. Her render’da yazılan fonksiyon ifadesi yine değerlendirilir; bağımlılıklar aynıysa React önceki saklanan fonksiyonu döndürür, değişirse bu render’daki fonksiyonu döndürür. Callback’i kendi başına çağırmaz.Kavramsal olarak
useCallback(fn, deps), useMemo(() => fn, deps) kullanımına denktir. Ancak bu durum, iki Hook'un aynı amaçla kullanıldığı anlamına gelmez: biri sonuç değeri, diğeri çağrılabilir fonksiyon verir.Somut örnek
Aşağıdaki örnekte liste filtreleme işlemi
useMemo ile, alt bileşene gönderilen seçim işlevi ise useCallback ile kararlı tutulur:
JavaScript:
import { memo, useCallback, useMemo, useState } from "react";
const UserList = memo(function UserList({ users, onSelect }) {
return users.map((user) => (
<button type="button" key={user.id} onClick={() => onSelect(user)}>
{user.name}
</button>
));
});
export default function UserSearch({ users }) {
const [query, setQuery] = useState("");
const [selectedId, setSelectedId] = useState(null);
const filteredUsers = useMemo(() => {
const normalizedQuery = query.trim().toLowerCase();
return users.filter((user) =>
user.name.toLowerCase().includes(normalizedQuery)
);
}, [users, query]);
const handleSelect = useCallback((user) => {
setSelectedId(user.id);
}, []);
return (
<>
<label>Kullanıcı ara
<input
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="Kullanıcı ara"
/>
</label>
<UserList users={filteredUsers} onSelect={handleSelect} />
{selectedId !== null && <p>Seçilen kimlik: {selectedId}</p>}
</>
);
}
Bu örnekte users ve query Object.is karşılaştırmasına göre değişmemişse önbellek korunabildiği sürece filtre sonucu yeniden kullanılır. React belirli durumlarda önbelleği bırakabilir; bu bir doğruluk veya kalıcı kimlik garantisi değildir.
handleSelect de bağımlılığı olmadığı için aynı referansı korur. Böylece React.memo kullanılan UserList, kendisine verilen özellikler gerçekten değişmedikçe gereksiz render yapmayabilir.React Compiler etkin bir projede değer ve fonksiyonların bir kısmı otomatik memoize edilebilir. Kurulumu kontrol edin; her yeni projede derleyicinin etkin olduğunu varsaymayın ve ölçmeden elle ekleme ya da topluca kaldırma yapmayın.
Hangisini ne zaman kullanmalı?
Karar verirken önce saklamak istediğiniz şeyin türüne bakın:
| İhtiyaç | Uygun Hook | Örnek |
|---|---|---|
| Hesaplanmış değer veya dizi üretimini azaltmak | useMemo | Filtreleme, sıralama, pahalı dönüştürme |
| Fonksiyon referansını korumak | useCallback | React.memo alt bileşenine callback geçirmek |
| Değeri veya fonksiyonu saklamaya gerek olmaması | Hiçbiri | Basit hesaplama ve sıradan event handler |
Uygulama kararı için şu sırayı izleyin:
- Önce bileşeni normal kodla yazın ve bağımlılıkları doğru belirleyin.
- Tekrar çalışması gerçekten pahalı olan hesaplamayı
useMemoiçine alın. - Bir callback'i alt bileşene aktarıyor ve alt bileşen referans karşılaştırması yapıyorsa
useCallbackkullanın. - Hook'un bağımlılık listesine callback'in veya hesaplamanın okuduğu tüm reaktif değerleri ekleyin.
- Optimizasyondan sonra davranışın değişmediğini ve gereksiz render'ın gerçekten azaldığını kontrol edin.
Sık yapılan hatalar
Her fonksiyona useCallback eklemek: Küçük bir event handler için çoğu zaman anlamlı kazanç sağlamaz. Gereksiz kullanım kodu karmaşıklaştırabilir.
useMemo ile yan etki çalıştırmak: Veri çekme, DOM değiştirme veya başka yan etkiler hesaplanmış değer üretme işi değildir. Bu işlemler için uygun yaşam döngüsü yaklaşımı kullanılmalıdır.
Eksik bağımlılık yazmak: Callback veya hesaplama dışarıdan okuduğu güncel bir değeri bağımlılık listesine koymazsa eski değerleri kullanabilir. Bu durumda referansın sabit kalması doğru davranışı garanti etmez.
useMemo'nun kalıcı depolama olduğunu sanmak: Bu Hook bir performans ipucudur; uygulamanın iş mantığı, kalıcı veri saklama veya zorunlu kimlik garantisi bunun üzerine kurulmaz.
Kaynaklar
Sık sorulan sorular
useMemo ve useCallback birlikte kullanılabilir mi?
Evet. Örneğin
useMemo ile oluşturulan bir filtrelenmiş listeyi, useCallback ile oluşturulan bir seçim işlevini aynı alt bileşene aktarabilirsiniz. Her birini yalnız kendi referansının veya hesaplamasının korunması gerektiğinde kullanın.useCallback kullanınca alt bileşen kesinlikle render olmaz mı?
Hayır. Alt bileşenin
React.memo ile sarmalanması, diğer özelliklerinin de değişmemesi ve bileşenin kendi state veya tükettiği context değerinin değişmemesi de önemlidir. Bu koşullarda bile memo render atlamayı mutlak garanti etmez. useCallback yalnızca fonksiyon referansındaki gereksiz değişimi önler.