React tətbiqlərində performans optimizasiyası üçün tez-tez istifadə olunan React.memo komponenti, düzgün istifadə edildikdə faydalı ola bilər. Lakin, bəzi hallarda bu alət gözlənilməz nəticələrə səbəb ola bilər. Bu məqalədə React.memo-nun necə işlədiyini, hansı hallarda effektiv olduğunu və hansı hallarda problem yaratdığını araşdıracağıq.
Memoizasiyanın vəd etdikləri
React tətbiqləri yavaşladıqda, proqramçılar tez-tez React.memo, useMemo və useCallback kimi memoizasiya alətlərinə müraciət edirlər. Bu alətlər, lazımsız yenidən renderlərin qarşısını almaqla performansı artırmaq məqsədi daşıyır. Lakin, bu alətlərin istifadəsi düşündüyümüzdən daha mürəkkəb ola bilər.
JavaScript-də referans müqayisələri
JavaScript-də primitiv dəyərlər (məsələn, ədədlər, sətirlər) dəyərə görə müqayisə olunur, obyektlər isə referansa görə:
// Primitiv dəyərlər dəyərə görə müqayisə olunur
const a = 1;
const b = 1;
console.log(a === b); // true
// Obyektlər referansa görə müqayisə olunur
const objA = { id: 1 };
const objB = { id: 1 };
console.log(objA === objB); // false, fərqli referanslar
Bu, React-də problem yarada bilər, çünki komponentlərə ötürülən obyekt və funksiyalar hər renderdə yeni referanslara sahib olur və bu, lazımsız yenidən renderlərə səbəb ola bilər.
useMemo və useCallback necə işləyir
React, referansların sabit qalmasını təmin etmək üçün useMemo və useCallback hook-larını təqdim edir.
useMemo: Məhsuldar (expensive) hesablamaların nəticəsini yadda saxlayır və yalnız asılılıqlar dəyişdikdə yenidən hesablayır.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback: Funksiyaların referansını yadda saxlayır və yalnız asılılıqlar dəyişdikdə yeni funksiyanı yaradır.
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
React.memo necə işləyir
React.memo yüksək səviyyəli bir komponentdir (HOC) və komponentin props-larını səthi (shallow) şəkildə müqayisə edərək, dəyişiklik olmadıqda yenidən renderin qarşısını alır:
const MyComponent = React.memo(function MyComponent(props) {
// komponentin implementasiyası
});
Lakin, əgər komponentə ötürülən props-lar hər renderdə yeni referanslara sahib olursa (məsələn, obyektlər və ya funksiyalar), React.memo bu dəyişiklikləri görəcək və komponenti yenidən render edəcək.
React.memo istifadəsində ümumi problemlər
1. Props-ların spreading-i
Props-ları yaymaq (spread) React.memo-nun effektivliyini poza bilər:
const Child = React.memo(({ data }) => {
// komponentin implementasiyası
});
const Parent = (props) => {
return <Child {...props} />;
};
Bu halda, Child komponentinə ötürülən props-ların referansları dəyişə bilər və bu, yenidən renderə səbəb olar.
2. children prop-u problemi
JSX-də children də bir prop-dur və hər renderdə yeni referansa sahib olur:
const MemoComponent = React.memo(({ children }) => {
// implementasiya
});
const Parent = () => {
return (
<MemoComponent>
<div>Some content</div>
</MemoComponent>
);
};
Bu halda, MemoComponent hər dəfə yenidən render olunacaq.
3. İç-içə memo komponentləri problemi
Bir-birinin içində yerləşən memo komponentlər də problem yarada bilər:
const InnerChild = React.memo(() => <div>Inner</div>);
const OuterChild = React.memo(({ children }) => <div>{children}</div>);
const Parent = () => {
return (
<OuterChild>
<InnerChild />
</OuterChild>
);
};
Bu halda, OuterChild hər dəfə yenidən render olunacaq, çünki InnerChild hər dəfə yeni bir JSX elementi yaradır.
Həll yolu: useMemo istifadə edərək InnerChild-ı yadda saxlamaq:
const Parent = () => {
const innerChild = useMemo(() => <InnerChild />, []);
return <OuterChild>{innerChild}</OuterChild>;
};
Memoizasiyanı nə zaman istifadə etməli
React.memo istifadə edin əgər:
Komponentiniz saf funksional komponentdirsə və eyni props-larla eyni nəticəni qaytarırsa.
Komponent tez-tez eyni props-larla render olunursa.
Render prosesi məhsuldardırsa (expensive).
Profilinq vasitəsilə performans problemi olduğunu təsdiqləmisinizsə.
useMemo istifadə edin əgər:
Məhsuldar bir hesablamanı hər renderdə təkrar etmək istəmirsinizsə.
Memoizə edilmiş komponentə ötürülən obyekt və ya array-ın sabit referansını saxlamaq istəyirsinizsə.
Hesablamanın həqiqətən məhsuldar olduğunu ölçüb təsdiqləmisinizsə.
useCallback istifadə edin əgər:
Optimallaşdırılmış child komponentlərinə referans bərabərliyinə əsaslanan callback-lar ötürürsünüzsə.
Callback useEffect hook-unda asılılıq kimi istifadə olunursa.
Memoizə edilmiş komponentlərdə sabit funksional referans saxlamaq istəyirsinizsə.
Alternativ: Komponent kompozisiyası
Memoizasiyadan əvvəl, komponent strukturunuzu kompozisiya vasitəsilə yaxşılaşdırmağı düşünün. Komponent kompozisiyası tez-tez performans problemlərini daha zərif şəkildə həll edir.
Məsələn, məhsuldar bir komponenti memoizə etmək əvəzinə, vəziyyəti daha spesifik bir konteynerə keçirin.