memo
, bileşenin prop’ları değişmediğinde yeniden render’lamayı atlamanıza izin verir.
const MemoizedComponent = memo(SomeComponent, arePropsEqual?)
Referans
memo(Component, arePropsEqual?)
Bir bileşenin önbelleğe alınmış (memoized) versiyonunu edinmek için ilgili bileşeni memo
‘ya sarın. Bileşeninizin önbelleğe alınmış bu versiyon, üst bileşen render olsa bile prop’ları değişmediği sürece genellikle yeniden render edilmez. Fakat React hala render edebilir: önbelleğe alma (memoization) render’ı engelleme garantisi değil, performans optimizasyonudur.
import { memo } from 'react';
const SomeComponent = memo(function SomeComponent(props) {
// ...
});
Daha fazla örnek için aşağıya bakınız.
Parametreler
-
Component
: Önbelleğe almak istediğiniz bileşendir.memo
bileşeni değiştirmez, önbelleğe alınmış yeni bileşen döndürür. Fonksiyon veforwardRef
dahil olmak üzere geçerli bir React bileşeni kabul eder. -
isteğe bağlı
arePropsEqual
: İki argüman kabul eden bir fonksiyondur: bileşenin eski ve yeni prop’ları. Eski ve yeni değerler aynıysa, bileşen yeni prop’lar ile eski prop’lardakiyle aynı çıktıyı üretecekse,true
döndürmelidir. Aksi takdirdefalse
döndürmelidir. Çoğu zaman bu fonksiyonu belirtmezsiniz. React, prop’ları varsayılan olarakObject.is
ile karşılaştırır.
Dönüş değerleri
memo
returns a new React component. It behaves the same as the component provided to memo
except that React will not always re-render it when its parent is being re-rendered unless its props have changed.
memo
yeni bir React bileşeni döndürür. memo
‘ya sağlanan bileşenle aynı davranır ancak istisna olarak üst bileşen render edildiğinde prop’ları değişmemişse yeniden render edilmez.
Kullanım
Prop’lar değişmediğinde yeniden render’ı önlemek
React, normalde üst bileşen yeniden render edildiğinde altındaki bileşenleri de render eder. memo
ile birlikte yeni prop’lar eskisiyle aynı olduğu sürece render edilmeyecek bileşen oluşturabilirsiniz. Bu tarz bileşenler, önbelleğe alınmış olarak nitelendirilir.
Bir bileşeni önbelleğe almak için, memo
‘ya sarmalayın ve döndürdüğü değeri orjinal bileşeninizin yerine kullanın:
const Greeting = memo(function Greeting({ name }) {
return <h1>Merhaba, {name}!</h1>;
});
export default Greeting;
React bileşeni her zaman pure render mantığına sahip olmalıdır. Yani prop’ları, state’i ve context’i değişmediği sürece aynı çıktıyı vermesi beklenir. memo
kullanarak React’a bu gerekliliğe uymasını söylersiniz. Prop’ları değişmediği takdirde React’ın yeniden render etmesine gerek yoktur. Bileşenin state’i ya da context’i değişirse memo
ile sarılmış olsa bile yeniden render olur.
Bu örnekte, Greeting
bileşeni name
değiştiğinde yeniden render olduğuna (çünkü prop’larından biri), ancak address
değiştiğinde olmadığına dikkat edin (çünkü Greeting
‘in prop’u değil):
import { memo, useState } from 'react'; export default function MyApp() { const [name, setName] = useState(''); const [address, setAddress] = useState(''); return ( <> <label> İsim{': '} <input value={name} onChange={e => setName(e.target.value)} /> </label> <label> Adres{': '} <input value={address} onChange={e => setAddress(e.target.value)} /> </label> <Greeting name={name} /> </> ); } const Greeting = memo(function Greeting({ name }) { console.log("Greeting'in render edildi:", new Date().toLocaleTimeString()); return <h3>Merhaba{name && ', '}{name}!</h3>; });
Derinlemesine İnceleme
Eğer uygulamanız bu site gibiyse ve çoğunlukla kaba etkileşimler içeriyorsa (sayfayı ve bölümü değiştirmek gibi), önbelleğe almak genellikle gereksizdir. Öte yandan, uygulamanız çizim editörü gibi daha küçük etkileşimler içeriyorsa (örneğin şekilleri taşıma gibi), önbelleğe almak çok faydalı olabilir.
memo
ile yapılan optimizasyon, yalnızca bileşeniniz sıkça aynı prop’larla yeniden render oluyorsa ve render mantığı pahallıysa değerlidir. Bileşeniniz yeniden render edildiğinde fark edilebilir bir gecikme yoksa önbelleğe almak gereksizdir. Unutmayın ki bileşene her seferinde farklı prop’lar (render esnasında tanımlanan fonksiyon veya nesne gibi) geçiyorsanız, memo
tamamen gereksizdir. Bu nedenle memo
ile birlikte useMemo
ve useCallback
‘e ihtiyacınız olacaktır.
Diğer durumlarda bileşeni memo
ile sarmalamanın faydası yoktur. Bunu yapmanın da önemli bir zararı yoktur, bu yüzden bazı ekipler durumları tek tek düşünmeyip mümkün olduğunca önbelleğe almayı tercih ederler. Bu yaklaşımın dezavantajı, kodun daha az okunabilir hale gelmesidir. Ayrıca tüm önbelleğe alma işlemleri etkili değildir: “her zaman yeni” olan tek bir değer tüm bileşenin önbelleğe alınmasını engellemeye yeterlidir.
Pratikte birkaç prensibi takip ederek çoğu önbelleğe alma işlemini gereksiz hale getirebilirsiniz:
- Bir bileşen diğer bileşenleri görsel olarak sarmalıyorsa, JSX’i alt eleman olarak kabul etmesine izin verin. Böylece sarmalayan bileşen kendi state’ini güncellediğinde, React alt bileşenlerin yeniden render edilmesinin gerekli olmadığını bilir.
- Mümkün olduğunca yerel state kullanın ve state’i gerektiğinden fazla yukarıya taşımayın. Örneğin, form gibi geçici state’leri veya fareyle bir öğenin üzerine gelindiği bilgisini ağacınızın en üstünde ya da global state kütüphanenizde tutmayın.
- Render mantığınızı saf tutun. Bileşeninizin render edilmesi bir soruna neden oluyorsa veya belirgin görsel farklılık oluşturuyorsa, bileşeninizde bir bug vardır! Önbelleğe almak yerine bug’ı çözün.
- State güncelleyen gereksiz efektlerden kaçının. React’ uygulamalarındaki çoğu performans sorunu, bileşenlerinizin defalarca render olmasına neden olan efekt zincirlerinden kaynaklanır.
- Efektlerinizden gereksiz bağımlılıkları kaldırmayı deneyin. Örneğin, efekt içerisindeki bazı nesne ve fonksiyonları bileşen dışarısına çıkarmak ön belleğe almaktan daha basittir.
Eğer spesifik bir etkileşim hala gecikmeli geliyorsa, React Developer Tools profiler kullanarak hangi bileşenlerin önbelleğe alındığında fayda sağlayacağını belirleyin ve ihtiyaç duyulan bileşenleri önbelleğe alın. These principles make your components easier to debug and understand, so it’s good to follow them in any case. In the long term, we’re researching doing granular memoization automatically to solve this once and for all.???
Ön belleğe alınmış (memoized) bileşeni state kullanarak güncelleme
Bir bileşen önbelleğe alınmış olsa bile kendi state’leri değiştiğinde yeniden render edilecektir. Önbelleğe almak, bileşene üst bileşenden iletilen yalnızca prop’larla ilgilidir.
import { memo, useState } from 'react'; export default function MyApp() { const [name, setName] = useState(''); const [address, setAddress] = useState(''); return ( <> <label> İsim{': '} <input value={name} onChange={e => setName(e.target.value)} /> </label> <label> Adres{': '} <input value={address} onChange={e => setAddress(e.target.value)} /> </label> <Greeting name={name} /> </> ); } const Greeting = memo(function Greeting({ name }) { console.log('Greeting render edildi:', new Date().toLocaleTimeString()); const [greeting, setGreeting] = useState('Merhaba'); return ( <> <h3>{greeting}{name && ', '}{name}!</h3> <GreetingSelector value={greeting} onChange={setGreeting} /> </> ); }); function GreetingSelector({ value, onChange }) { return ( <> <label> <input type="radio" checked={value === 'Merhaba'} onChange={e => onChange('Merhaba')} /> Normal karşılama </label> <label> <input type="radio" checked={value === 'Merhaba, hoşgeldin'} onChange={e => onChange('Merhaba, hoşgeldin')} /> İçten karşılama </label> </> ); }
Bir state değişkenini mevcut değerine yeniden ayarlarsanız, React memo
olmasa bile bileşeninizin yeniden render’ını atlar. Bileşen fonlsiyonunuzun fazladan çağırıldığını görebilirsiniz ancak sonuç gözardı edilir.
Ön belleğe alınmış (memoized) bileşeni bağlam (context) kullanarak güncelleme
Bir bileşen önbelleğe alındığında bile, kullandığı context değiştiğinde yeniden render olur. Önbelleğe almak, bileşene üst bileşenden iletilen yalnızca prop’larla ilgilidir.
import { createContext, memo, useContext, useState } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { const [theme, setTheme] = useState('dark'); function handleClick() { setTheme(theme === 'dark' ? 'light' : 'dark'); } return ( <ThemeContext.Provider value={theme}> <button onClick={handleClick}> Temayı değiştir </button> <Greeting name="Seher" /> </ThemeContext.Provider> ); } const Greeting = memo(function Greeting({ name }) { console.log("Greeting render edildi:", new Date().toLocaleTimeString()); const theme = useContext(ThemeContext); return ( <h3 className={theme}>Merhaba, {name}!</h3> ); });
Bileşeninizin yalnızca context’in bazı öğeleri değiştiğinde yeniden render edilmesini sağlamak için bileşeni ikiye bölün. Dış bileşendeki context’den ihtiyacınız olanı okuyun ve önbelleğe alınmış alt bileşene prop olarak iletin.
Prop değişikliklerini en aza indirme
memo
kullandığınızda, herhangi bir prop önceki değerine sığ olarak eşit (shallowly equal) değilse bileşeniniz yeniden render edilir. React Object.is
karşılaştırmasını kullanarak bileşeninizdeki her prop’u önceki değeriyle karşılaştırır. Object.is(3, 3)
sonucunun true
, Object.is({}, {})
sonucunun false
olduğuna dikkat edin.
memo
‘dan en iyi şekilde yararlanmak için, prop’ların değişme sayısını en aza indirin. Örneğin, prop bir nesne ise useMemo
kullanarak üst bileşenin her seferinde nesneyi yeniden oluşturmasını önleyebilirsiniz.
function Page() {
const [name, setName] = useState('Seher');
const [age, setAge] = useState(24);
const person = useMemo(
() => ({ name, age }),
[name, age]
);
return <Profile person={person} />;
}
const Profile = memo(function Profile({ person }) {
// ...
});
Prop değişikliklerini en aza indirmenin daha iyi bir yolu, gereken minimum bilgiyi prop olarak kabul ettiğinden emin olmaktır. Örneğin, bütün nesne yerine değerleri tek tek kabul edebilir:
function Page() {
const [name, setName] = useState('Seher');
const [age, setAge] = useState(24);
return <Profile name={name} age={age} />;
}
const Profile = memo(function Profile({ name, age }) {
// ...
});
Bazen değerler daha az değişen biçimlere çevrilebilir. Örneğin, buradaki bileşen değerin kendisinden ziyade var olduğunu gösteren bir boolean değer kabul eder:
function GroupsLanding({ person }) {
const hasGroups = person.groups !== null;
return <CallToAction hasGroups={hasGroups} />;
}
const CallToAction = memo(function CallToAction({ hasGroups }) {
// ...
});
Önbelleğe alınan bileşene fonksiyon iletmeniz gerektiğinde, hiçbir zaman değişmemesi için bileşen dışında tanımlayın veya render’lar arasında tanımı önbelleğe almak için useCallback
kullanın.
Özel karşılaştırma fonksiyonu belirtme
Önbelleğe alınan bileşenin prop değişikliklerini en aza indirmek bazen mümkün olmayabilir. Bu durumda sığ eşitliği kullanmak yerine (shallow equality) eski ve yeni prop’ları karşılaştırmak için özel bir karşılaştırma fonksiyonu sağlayabilirsiniz. Bu fonksiyon, memo
‘ya ikinci argüman olarak iletilir. Yalnızca yeni prop’ların eskileri ile aynı çıktıyı verdiği durumlarda true
, aksi takdirde false
döndürmelidir.
const Chart = memo(function Chart({ dataPoints }) {
// ...
}, arePropsEqual);
function arePropsEqual(oldProps, newProps) {
return (
oldProps.dataPoints.length === newProps.dataPoints.length &&
oldProps.dataPoints.every((oldPoint, index) => {
const newPoint = newProps.dataPoints[index];
return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;
})
);
}
Bunu kullanırsanız karşılaştırma fonksiyonunun yeniden render etmeden daha hızlı olup olmadığını kontrol etmek için tarayıcınızın geliştirici araçlarındaki Performans panelini kullanın. Süpriz yaşayabilirsiniz.
Performans ölçümleri yaparken, React’ın canlı ortam (production) modunda çalıştığından emin olun.
Sorun giderme
Prop bir nesne, dizi veya fonksiyon olduğunda bileşenim yeniden render’lanıyor
React eski ve yeni prop’ları sığ karşılaştırma ile kıyaslar: her yeni prop’un eski prop’a referans olarak eşit olup olmadığına bakar. Eğer üst eleman her render olduğu zaman eskisiyle birebir aynı olan yeni bir nesne veya dizi oluşturuyorsanız, React değiştirildiğini düşünür. Benzer şekilde, üst bileşen render edildiğinde yeni bir fonksiyon oluşturuyorsanız, React aynı tanıma sahip olsa dahi değiştiğini düşünür. Bunu önlemek için, prop’ları basitleştirin veya üst bileşendeki prop’ları önbelleğe alın.