React Memoization-React.memo() Nasıl Kullanılır ?
Başlangıçta, React.Memo()’nun React uygulamalarında nasıl kullanılacağını anlamak için memoization’ın ne olduğuna bir göz atmalıyız.
Memoization
Memoization temeli nedir, neye dayanır? Bu sorunun cevabını Dinamik Programlama olarak verebiliriz. Dinamik Programlama, kodunuzun basit özyineleme (recursion) üzerinden optimizasyonu anlamına gelir. Buradaki fikir, alt problemin sonuçlarını saklamak ve daha sonra aynı hesaplamaları yapmak için gerektiğinde yeniden kullanmaktır. Dinamik Programlama, memoization kullanarak programını daha verimli hale getirir. Memoization’ı daha iyi anlamak için basit bir örnek alalım. Diyelim ki addition adında aldığı iki parametreyi toplayıp return eden bir fonksiyonumuz olsun:
Temelde toplama fonksiyonu yukarıda da gördüğünüz gibi bir toplama işlemi yapar. Toplama işlevi iki argüman alır. Bu örnekte, 5 ve 10, bir argüman olarak addition fonksiyonuna iletilir. Fonksiyonun mantıksal işlemi uygulandıktan sonra 15 değerini döndürür.
Bu fonksiyonu memoization tekniklerinden birini kullanarak çağırırsak, 5 ve 10 değerlerine karşılık gelen 15 değeri kaydedilir. Bu yüzden bu fonksiyonu tekrar aynı parametrelerle çağırırsak parametre değerlerine karşılık gelen sonuca sahip olduğumuz için sonucu direkt bize döndürür ve tekrar toplama işlemini yapmaz.
React Bileşeninin Gereksiz Re-Render Olma Durumu
React bileşeninin gereksiz yere re-render olma durumunu anlamak için basit bir uygulama yapalım. Bu uygulamanın yapısı:
/src
/components
MyBestFriends.jsx
Counter.jsx
App.js
Uygulamada iki bileşen kullanılmaktadır. MyBestFriends bileşeni, en iyi arkadaşları listelemek ve yeni en iyi arkadaşlar eklemek için kullanılır. Bir diğer bileşen olan Counter ise bir buton kullanarak sayacı artırmak ve sayaçtaki değeri göstermek için kullanılır. Bu bileşenlerin her ikisi de App.js’de çağrılır.

App.js Kaynak Kodu:
App.js’de count ve myBestFriendList olmak üzere iki adet state yapımız bulunmaktadır. Bu stateler, varsayılan değerleri ile diğer bileşenlere geçiş için oluşturulmuştur. Count state’i, sayaç yazdırma ve sayacı artırma için Counter bileşeninden sorumludur, myBestFriendList state’i ise , en iyi arkadaşların listesini yazdırmak ve yeni en iyi arkadaşı eklemek için MyBestFriendList bileşeninden sorumludur.
MyBestFriend.jsx Kaynak Kodu:
MyBestFriend bileşeni, myBestFriendList state’ini ve bu state’in güncelleme işlemini gerçekleştiren setMyBestFriendList fonksiyonunu alır. MyBestFriendList state’in amacı, en iyi arkadaşların yazılı olduğu listeyi göstermek, setMyBestFriendList fonksiyonunun amacı ise yeni bir en iyi arkadaş eklemek ve halihazırda bulunan en iyi arkadaşlar listesini güncellemektir. Ayrıca MyBestFriend bileşeninin, yeni en iyi arkadaşın adını almak ve myBestFriendList state’ine eklemek için kullanılan newBestFriend state’i de bulunmaktadır.
Counter.jsx Kaynak Kodu:
Counter bileşeni, count state’ini ve bu state’i güncelleme işlevini gören setCount fonksiyonunu props olarak alır. Count state’inin amacı, bu sayım değerini yazdırmaktır. setCount fonksiyonunun amacı, sayım değerini artırmak ve count state’ini güncellemektir.
Kaynak kodların açıklamasından sonra uygulamanın nasıl çalıştığını görelim.

Her şeyden önce, uygulama herhangi bir sorun olmadan çalışmaktadır. Sayım değerini gösteren ve sayım değerini arttırma, ayrıca en iyi arkadaşları oluşturma ve yeni en iyi arkadaş ekleme gereksinimlerini sağlamaktadır. Konsoldaki ilk render olduklarında nasıl bir sonuç verdiklerine bakalım.

Daha sonra butona tıklayalım ve sayım durumunu güncellediğimizde neler olduğunu kontrol edelim. Yukarıda bahsettiğim gibi, App.js’de count state’i oluşturuluyor ve Counter bileşenine props olarak gönderiliyor. Count state’i güncellendiğinde App bileşeninin yeniden oluşturulmasını bekliyoruz. Bu noktaya kadar her şey yolunda. Neler olduğunu kontrol edelim.

Sayımı arttırmak için her butona bastığımızda counter state’i güncellenmektedir ve bu da App bileşeninin re-render olmasına sebep olmaktadır.
MyBestFriends bileşeninde herhangi bir değişiklik yapmasak bile, bu bileşeninin de re-render olduğunu görebiliriz çünkü App bileşenimiz içinde bulundurduğu count state’inin güncellemesinden dolayı re-render olmuştur.

Ayrıca MyBestFriends bileşeni için de aynı durum geçerlidir. MyBestFriendList state’ini güncellediğimizde Counter bileşeninde herhangi bir değişiklik yapmasak bile re-render edildiğini görüyoruz.
Karşılaştığımız sorunu özetleyecek olursak, eğer count state’ini güncelliyorsam sadece etki ettiği Count bileşeninin re-render olmasını beklerim ama MyBestFriends bileşeninin count state’i ile bir alakası olmadığı için re-render olmasını beklemem ve istemem. Aynı durum myBestFriend state’inin güncellenmesinde de geçerli.
Düşünsenize ben sayımı arttırmak için butona 1 milyon kez basıyorum ve 1 milyon kez MyBestFriends bileşeni istememize rağmen re-render olacak ve uygulamaya performans kaybı olarak yansayacak.
Bu tür durumlar küçük uygulamalarda sorun gibi görünmese de göz ardı edilebilecek gibi görünen bu sorunlar uygulamanız büyüdükçe uygulamanızın performansını ciddi bir şekilde etkileyebilir.
Yukarıda bahsedilen performans kaybının önüne geçebilmek için React’ın bize sunduğu React.memo() yapısını kullanabiliriz.
React.memo()
Eğer fonksiyonel bir bileşen aynı propsları alır ve aynı sonuçları verirse, gereksiz re-render olma durumunu atlamak için React.memo()’yu kullanabiliriz. React.memo()’nun bunu nasıl başardığını açıklayalım.
React.memo(), önceki prop ile mevcut prop’u karşılaştırmak için shallow karşılaştırmayı kullanır. Shallow karşılaştırma string, number gibi primitive tipte olan önceki prop ve mevcut prop değerlerinin eşitliğine bakar veya referans tipte olan önceki prop ve mevcut prop’un referanslarının eşitliğini kontrol eder. Eğer propslar shallow karşılaştırma tarafından eşitse, return değeri true aksi halde false döner. Daha sonra , shallow karşılaştırmanın sonucu eğer true ise React.memo(), memoziation tekniğini kullanarak bileşenin gereksiz yere re-render olmasının önüne geçer.
React.memo() Yapısının Proje İçinde Kullanılması
İlk olarak, React.memo() high-order bir bileşendir. Aynı propsları alan ve aynı sonucu veren fonksiyonel bir bileşende bu yapıyı kullanabiliriz. Count state’ini güncellediğimizde, MyBestFriends bileşeninin re-render olmasını önlemek için MyBestFriends bileşenini React.memo() ile sarmallayacağız. Bu sayede App.js’de myBestFriendList state’inde herhangi bir güncelleme olmadığı durumlarda MyBestFriends bileşeninin re-render olmasını engellemiş olacağız.
React.memo(), MyBestFriends bileşenine gönderilen props değerlerini, bileşen oluşturulurkenki önceki props değerleriyle karşılaştırır. Eğer önceki props değerleri ile şimdiki props değerleri aynı ise bileşenin re-render olma durumunu engellememize yardımcı olur. Böylece, bileşen çıktısının değişikliğine sebep olacak farklı props değerleri gönderilene kadar MyBestFriends bileşeninin yeniden oluşturulmasını engeller. Count state’ini tekrar güncelleyerek sonucu görelim.

Yukarıdan da görebileceğiniz gibi, React.memo() memoization tekniğini kullanarak yalnızca Counter bileşenini re-render eder, MyBestFriends bileşeninin önceki props referans değerleri ile şimdiki props referans değerleri aynı olduğu için MyBestFriends bileşeni re-render edilmez. MyBestFriendList state güncellemesi nedeniyle Counter bileşeninin re-render olmasını önlemek için React.memo() yapısını Counter bileşenine uygulayalım.
Bundan sonra myBestFriendList durumunu güncellediğimizde sadece MyBestFriends bileşeninin re-render olmasını bekliyoruz.

Ve sadece MyBestFriends bileşeninin re-render olduğunu görebilmekteyiz.
Ne Zaman React.memo() Kullanmalıyız ?
1- Aynı props değerlerini alan ve aynı çıktıyı veren fonksiyonel bir bileşenimiz varsa.
2- Eğer bileşen gereksiz yere re-render oluyorsa (bu durum uygulamanız büyüdükçe uygulamanızın performansını etkiler).
Ne Zaman React.memo() Kullanmamalıyız ?
1- Bileşeniniz class tabanlıysa, shouldComponentUpdate() öğesini kullanın.
2- Farklı props değerlerini alan ve farklı çıktılar veren fonksiyonel bir bileşenimiz varsa.
3- Eğer tüm fonksiyonel bileşenleri React.memo() ile sarmallarsak, performans sorununa neden olur.
Bir düşünelim, 100 fonksiyonel bileşenimiz var ve hepsi farklı props değerleri alıyor. React.memo()’nun props değerlerini karşılaştırmak için shallow karşılaştırma kullandığını unutmayın. Mevcut proplar ve önceki proplar eşitse (primitive tip için değerleri, referans tip için referansları kontrol eder), o zaman shallow karşılaştırma true döndürür, aksi takdirde false döndürür. Ancak bu senaryoda, farklı props değerleri nedeniyle oluşturulan çıktı her zaman farklıdır, bu nedenle shallow karşılaştırma dönüşlerinin her zaman false döneceğini biliyoruz. Shallow karşılaştırma 100 kez boşuna çalışır. Sonuç olarak, gereksiz React.memo() kullanmak performans açısından uygulamaya zarar verir.
Yazarlar
Mustafa Batuhan Bayoğlu: Linkedln — Github
Gökhan Samet Albayrak: Linkedln — Github — Medium
Son olarak bu yazıyı Batuhan ile beraber yazdım ve kendisine çok teşekkür ediyorum. Bu makalenin React.memo() hakkında fikir sahibi olmanızı ve size değer katmasını umuyoruz. Bizimle paylaşmak istediğiniz herhangi bir nokta varsa geri bildirime tamamen açığız.
