useMemo, bir hesaplamanın sonucunu render'lar arasında önbelleğe alır. React fonksiyonunu ilk render'da çalıştırır, sonucu tutar ve bağımlılık dizisindeki değerlerden biri değişene kadar sonraki render'larda aynı sonucu geri verir.
Console'u aç ve temayı birkaç kez değiştir: hiçbir şey kaydedilmez ve değiştirme anında olur. Sekme değiştir; kısa bir duraklamayla filtering for active görünür, çünkü tab bir bağımlılıktır. Şimdi useMemo satırını const visible = filterTodos(todos, tab); ile değiştir ve temayı yeniden değiştir: her tıklama kayıt yazar ve 100 ms takılır.
Sözdizimi
const cachedValue = useMemo(calculateValue, dependencies);
const visible = useMemo(() => filterTodos(todos, tab), [todos, tab]);
calculateValue, önbelleğe almak istediğin değeri döndüren argümansız bir fonksiyondur. React onu render sırasında çağırır.dependencies, fonksiyonun okuduğu, bileşenden gelen her değerin listesidir: props, state ve bileşen gövdesinde tanımlanan değişkenler.- Her render'da React her bağımlılığı önceki render'daki değeriyle
Object.iskullanarak karşılaştırır. Hepsi eşleşirse fonksiyonunu çağırmadan önbelleğe alınmış değeri döndürür. Herhangi biri farklıysa fonksiyonu çağırır ve yeni sonucu önbelleğe alır.
useMemo bir hook'tur, bu yüzden hook kurallarına uyar: onu bileşenin en üst düzeyinde çağır, bir döngünün ya da koşulun içinde değil.
Bağımlılıklar eksiksiz olmalı
Diziden bir değeri dışarıda bırak; önbellek eski değerinden hesaplanmış bir sonucu sunmaya devam eder. Burada toplam qty'yi okuyor ama yalnızca price'ı listeliyor:
Quantity'ye tıkla: 2'ye çıkar, ama toplam 10'da kalır ve hiçbir şey kaydedilmez. Price'a tıkla; toplam 22'ye atlar, çünkü hesaplama ancak o zaman, artık mevcut qty'yi okuyarak yeniden çalışır. Diziye qty'yi ekle; iki düğme de toplamı günceller. react-hooks/exhaustive-deps lint kuralı gerçek bir projede bu hatayı yakalar.
Ters hata, her render'da yeni olan bir bağımlılıktır; örneğin bileşen gövdesinde oluşturulan bir nesne. Önceki değeriyle asla eşleşmez, bu yüzden önbellek asla kullanılmaz:
const filters = { tab, query }; // a new object every render
const visible = useMemo(() => applyFilters(todos, filters), [todos, filters]); // recomputes every time
// List the primitive values instead
const visible = useMemo(() => applyFilters(todos, { tab, query }), [todos, tab, query]);
Bir memo çocuğu için bir nesneyi sabit tutmak
useMemo'ya başvurmanın ikinci nedeninin yavaş hesaplamalarla hiçbir ilgisi yoktur. memo ile sarılmış bir çocuk, render'ını yalnızca her prop geçen seferkiyle aynı olduğunda atlar. Bileşen gövdesinde yazılan bir nesne literal'i her render'da yeni bir nesnedir, bu yüzden içeriği değişmemiş olsa bile memo'yu boşa çıkarır. useMemo, girdileri değişene kadar aynı nesneyi tutar.
Not alanına yaz; Chart sessiz kalır, Switch unit ise bir render kaydeder. useMemo çağrısını düz bir const options = { unit, max: unit === 'km' ? 100 : 62 }; ile değiştir; nottaki her tuş vuruşu render Chart kaydeder.
Aynısı bir nesneye bağlı bir efekt için de geçerlidir. Bir efekt bağımlılıklarında options'ı listeliyorsa ve options her render'da yeniden oluşturuluyorsa efekt her render'dan sonra çalışır. useMemo bunu düzeltir, ama daha basit çözüm çoğu zaman nesneyi efektin içinde oluşturmak ve ilkel değerlere bağımlı olmaktır:
useEffect(() => {
const options = { serverUrl, roomId };
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [serverUrl, roomId]);
useMemo ne zaman kullanılmamalı
Bir bileşendeki çoğu hesaplama ucuzdur: sayıları toplamak, iki metni birleştirmek, birkaç düzine öğeyi filtrelemek. Bunları önbelleğe almak neredeyse hiçbir şey kazandırmaz: React yine de değeri saklar ve her render'da bağımlılıkları karşılaştırır, kod da okunması daha zor hâle gelir.
// No useMemo needed
const fullName = first + ' ' + last;
const doneCount = todos.filter((t) => t.done).length;
Bir şeyin yavaş olup olmadığını öğrenmek için onu ölç. Hesaplamayı console.time('filter') ve console.timeEnd('filter') ile sar, etkileşimi çalıştır ve sayıya bak. Kabaca bir rehber olarak, makinende bir milisaniye ya da daha fazla süren iş bir adaydır; kullanıcılarının cihazlarının daha yavaş olabileceğini akılda tut. useMemo'ya başvurmadan önce işin render'a ait olup olmadığını da kontrol et: hiç değişmeyen veri bileşenin dışında bir kez hesaplanabilir, sayfanın yalnızca küçük bir kısmının kullandığı state ise daha küçük bir bileşene taşınabilir (bkz. render).
Türetilmiş değerler için useMemo ve useEffect
Yaygın bir kalıp, bir değeri bir efektte hesaplayıp state'te saklamaktır:
// Two renders per change, and the first one has stale data
const [visible, setVisible] = useState([]);
useEffect(() => {
setVisible(filterTodos(todos, tab));
}, [todos, tab]);
// One render, always in sync
const visible = useMemo(() => filterTodos(todos, tab), [todos, tab]);
Efekt sürümü bir kez eski visible ile render eder, sonra efekti çalıştırır, state ayarlar ve yeniden render eder. Bu fazladan bir render'dır ve ilkinde liste sekmeyle eşleşmez. useMemo ile (ya da iş ucuzsa hiç hook olmadan) render sırasında hesaplamak doğru değeri aynı render'da verir. Efektler bir state parçasını diğerinden türetmek için değil, React dışındaki şeylerle senkronize olmak içindir.
Bir garanti değil, bir performans ipucu
useMemo, React'e bir sonucu yeniden kullanabileceğini söyler. Kullanacağına söz vermez. React normal işleyişte önbelleğe alınmış değeri tutar, ama onu atabilir; örneğin geliştirme sırasında dosyayı düzenlediğinde ya da bir bileşen ilk mount'u sırasında askıya alındığında. Kodu hesaplama yeniden çalışırsa da doğru olacak şekilde yaz: fonksiyon saf olmalı, istekler, abonelikler ya da dışındaki değişkenleri değiştirmek gibi yan etkileri olmamalıdır. Yan etkiler bir olay işleyicisine ya da bir efekte aittir.
Geliştirmede, StrictMode altında React, saf olmayan kodu fark etmene yardım etmek için hesaplama fonksiyonunu iki kez çağırır. Buradaki önizleme bir production derlemesi gibi çalışır, bu yüzden her filtering for kaydını bir kez görürsün.
useMemo ve React Compiler
React Compiler 1.0, bileşenlerini analiz eden ve memoization'ı otomatik olarak ekleyen bir derleme aracıdır; useMemo, useCallback ve memo'nun yardımcı olacağı yerlerde hesaplamaları ve JSX'i önbelleğe alır. Onu kullanan bir projede bunlardan daha azını elle yazarsın. Manuel useMemo compiler ile birlikte hâlâ çalışır, compiler kurulu değilken hâlâ kullanılacak araçtır ve mevcut React kodunun çoğunda onunla karşılaşacaksın.
Sıkça Sorulan Sorular
React'te useMemo ne yapar?
Hesaplama fonksiyonunu ilk render'da çağırır, sonucu saklar ve sonraki render'larda bağımlılık dizisindeki her değer geçen seferkiyle aynı olduğu sürece (Object.is ile karşılaştırılır) saklanan sonucu döndürür.
useMemo'yu ne zaman kullanmalıyım?
Bir hesaplama gözle görülür şekilde yavaş olduğunda ve girdileri nadiren değiştiğinde, ya da bir memo çocuğu veya bir efekt ona bağlı olduğu için bir nesnenin ya da dizinin render'lar arasında aynı referansı koruması gerektiğinde. Ucuz işler için onu atla.
useMemo ile useCallback arasındaki fark nedir?
useMemo, fonksiyonunun döndürdüğü değeri önbelleğe alır. useCallback fonksiyonun kendisini önbelleğe alır. useCallback(fn, deps), useMemo(() => fn, deps) ile aynıdır.
useMemo değerin asla yeniden hesaplanmayacağını garanti eder mi?
Hayır. Bir performans ipucudur. React bazı durumlarda önbelleğe alınmış değeri atabilir, örneğin geliştirmede dosyayı düzenlediğinde; bu yüzden kodun hesaplama yeniden çalışırsa da doğru çalışmalıdır.
React Compiler ile hâlâ useMemo'ya ihtiyacım var mı?
Derlemen kullandığında React Compiler memoization'ı otomatik olarak ekleyebilir. Compiler olmadan ya da atladığı durumlar için manuel useMemo aynı şekilde çalışmaya devam eder ve mevcut kodda hâlâ yaygındır.