memo, props'u geçen seferkiyle aynı olduğunda bir bileşenin yeniden render'ı atlamasını sağlar. Bir bileşeni memo(...) ile sar; ebeveyni render olduğunda React önce her yeni prop'u eskisiyle karşılaştırır, hepsi eşitse bileşeni yeniden çağırmak yerine önceki sonucu yeniden kullanır.
Input'a yaz: hızına yetişir ve Console render SlowList'i yalnızca bir kez, ilk render'dan gösterir. memo( sarmalayıcısını (ve kapanış parantezini) kaldır ve yeniden yaz: her tuş vuruşu artık liste için 150 ms bekler ve bir render kaydeder.
Sözdizimi
import { memo } from 'react';
const MemoizedComponent = memo(SomeComponent, arePropsEqual?);
SomeComponentherhangi bir fonksiyon bileşenidir. Olağan stil tanımı doğrudan sarmaktır:const Card = memo(function Card(props) { ... }). İçteki fonksiyona ad vermek adı React DevTools'ta ve hata mesajlarında korur.arePropsEqualisteğe bağlıdır ve nadiren gerekir (aşağıya bak).- Sonuç, diğer her bileşen gibi render ettiğin bir bileşendir.
Varsayılan olarak React eski ve yeni props'u sığ (shallow) olarak karşılaştırır: her prop'u Object.is ile kontrol eder. Metinler, sayılar ve boolean'lar değere göre karşılaştırılır, yani title="Hi", title="Hi"'ye eşittir. Nesneler, diziler ve fonksiyonlar referansa göre karşılaştırılır: içeriği aynı olan iki nesne yine de farklı nesnelerdir.
Yukarıdaki örnekte items bileşenin dışında tanımlanmıştır, bu yüzden her render'da aynı dizidir ve karşılaştırma geçer.
Yeni nesneler ve fonksiyonlar memo'yu boşa çıkarır
Ebeveynin gövdesinde literal olarak yazılan her şey her render'da yeniden oluşturulur: { name: 'Ada' }, [1, 2, 3], () => save(). Birini bir memo bileşenine aktar; prop öncekine asla eşit olmaz.
Her tıklama render Badge object prop kaydeder ama metin sürümünü asla kaydetmez. Nesne durumunu düzeltmenin üç yolu var:
- İlkel değerler aktar. Çocuğun yalnızca bir iki alana ihtiyacı varsa
user={{ name: 'Ada' }}yerinename="Ada". - Sabitleri bileşenin dışına taşı. Hiç değişmeyen bir nesne, ilk örnekteki
itemsgibi modül düzeyinde yaşayabilir. - Onu önbelleğe al. Props ya da state'ten oluşturulan nesneleri ve dizileri useMemo ile, fonksiyonları useCallback ile sar.
children'ın da aynı sorunu vardır. <Card><p>Hello</p></Card> her render'da yeni bir <p> öğe nesnesi oluşturur, bu yüzden children alan bir memo Card her seferinde yeniden render olur.
memo'nun durdurmadıkları
memo yalnızca ebeveynden gelen props'a bakar. Memoize edilmiş bir bileşen yine de şu durumlarda render olur:
- kendi state'i değiştiğinde, kendi
useStateya dauseReducer'ı aracılığıyla; - okuduğu bir context değiştiğinde,
useContextaracılığıyla.
Unrelated'a tıkla; Panel atlanır. Temayı değiştir; render olur, çünkü context'i okur. Like'a tıkla; render olur, çünkü kendi state'i değişti. Amaçlanan davranış budur: memo, bileşenin bağlı olduğu hiçbir şey değişmediğinde işi atlamakla ilgilidir; context ve state ise onun bağlı olduğu şeylerdir.
Özel bir karşılaştırma fonksiyonu
İkinci argüman sığ karşılaştırmanın yerini alır. Önceki ve sonraki props'u alır ve eşit sayılmaları gerektiğinde true döndürür; bu da React'in render'ı atlaması demektir.
const MemoChart = memo(Chart, (prev, next) => {
return (
prev.points.length === next.points.length &&
prev.points.every((p, i) => p === next.points[i]) &&
prev.color === next.color &&
prev.onSelect === next.onSelect
);
});
Buna nadiren ihtiyacın olur ve yanlış yapması kolaydır. Fonksiyon bir prop'u yok sayarsa bileşen o prop'un eski değerini göstermeye devam eder. Bir fonksiyon prop'unu yok saymak en kötü durumdur: atlanan bileşen eski state'i okuyan eski fonksiyonu çağırmaya devam eder. Her prop'u karşılaştır ve büyük veriler üzerinde derin eşitlik kontrolleri yapma, çünkü o zaman karşılaştırma kurtardığı render'dan daha pahalıya mal olur. useMemo ve useCallback ile sabit props neredeyse her zaman daha iyi bir çözümdür.
memo'nun çalıştığını kontrol etmek
En hızlı kontrol bu sayfada kullanılandır: bileşenin en üstünde bir console.log. Bir atlama beklerken yazdırıyorsa props'u da kaydet ve değişeni bulmak için öncekilerle karşılaştır. Gerçek bir projede React DevTools daha fazla yardımcı olur: her etkileşimde hangi bileşenlerin yanıp söndüğünü görmek için ayarlarında "Highlight updates when components render" seçeneğini aç; Profiler sekmesi de her render'ı ve, ilgili ayar açıkken, her bileşenin neden render olduğunu kaydeder.
İki şey memo'nun başarısız olması gibi görünür ama değildir:
- İlk render her zaman olur.
memoyalnızca bileşen bu props ile bir kez render olduktan sonraki render'ları atlar. - Geliştirme derlemeleri daha fazla render edebilir. StrictMode altında React, saf olmayan kodu yakalamak için geliştirmede her bileşeni iki kez render eder. Buradaki önizleme bir production derlemesi gibi çalışır, bu yüzden yukarıdaki her kayıt satırı bir gerçek render demektir.
memo ne zaman kullanılmalı
Bir bileşenin render'ı yavaş olduğunda ve ebeveyni onun için sık sık aynı props ile render olduğunda memo değer: bir input'un yanındaki uzun bir liste ya da bir grafik, her saniye güncellenen bir sayfanın içindeki bir kenar çubuğu. Hızlı render olan ya da props'u zaten her seferinde değişen bileşenler için bir karşılaştırma ekler ve hiçbir şey kazandırmaz. Memoize etmeden önce bunun yerine yeniden yapılandırıp yapılandıramayacağına bak; render sayfasında gösterildiği gibi state'i aşağı taşıyarak ya da JSX'i children olarak aktararak.
Projen kullanıyorsa React Compiler 1.0, bileşenleri ve props'larını derleme zamanında otomatik olarak memoize eder; bu da çoğu manuel memo çağrısını gereksiz kılar. O olmadan memo tam olarak burada anlatıldığı gibi çalışır ve React kodunda yaygın olmaya devam eder.
Sıkça Sorulan Sorular
React.memo ne yapar?
Bir bileşenin memoize edilmiş bir sürümünü döndürür. Ebeveyn render olduğunda React yeni props'u öncekilerle karşılaştırır ve her prop Object.is'e göre eşitse bileşenin render'ını atlar.
memo bileşenim neden hâlâ yeniden render oluyor?
Genellikle bir prop, ebeveynin render'ı sırasında oluşturulan yeni bir nesne, dizi ya da fonksiyon olduğu için; bu yüzden öncekine asla eşit olmaz. Bileşen ayrıca, memo olsun ya da olmasın, kendi state'i değiştiğinde ya da okuduğu bir context değiştiğinde render olur.
React.memo ile useMemo arasındaki fark nedir?
memo bir bileşeni sarar ve render'ını atlar. useMemo bir bileşenin içinde hesaplanmış bir değeri önbelleğe alan bir hook'tur. Çoğu zaman birlikte çalışırlar: useMemo bir nesne prop'unu sabit tutar, böylece memo atlayabilir.
Her bileşeni memo ile sarmalı mıyım?
Hayır. Çoğu render ucuzdur ve prop karşılaştırmasının kendi maliyeti vardır. Onu render'ı yavaş olan ve sık sık aynı props'u alan bileşenler için kullan.
memo children ile çalışır mı?
Nadiren. children olarak aktarılan JSX her render'da yeni bir nesnedir, bu yüzden children alan bir memo bileşeni neredeyse her zaman yeniden render olur.