React.memo, useMemo ve useCallback

Oğuzhan Uyanık
Kodcular
Published in
4 min readAug 15, 2023

Bu yazıda, React projelerinin optimizasyonunda kullanılan birbiriyle ilişkili bazı kavramları inceleyeceğiz. Bunlar; Memoization, React.memo, useMemo ve useCallback’dir.

Memoization

Bir optimizasyon tekniği olan memoization, kelime anlamı olarak “hafızada tutmak” veya “ezberlemek” gibi anlamlara sahiptir. React söz konusu olduğunda, bu teknik ile fonksiyonların ve bileşenlerin gereksiz yere render edilmesi önlenir.

React.memo

React’da bir bileşen render edildiğinde, o bileşenin içerisindeki diğer bileşenler de render edilir. Alt bileşenlerin tekrar render edilmesi çoğu zaman gerekli değildir. Bu gibi durumlarda, gereksiz render işlemini engellemek için React.memo kullanılır.

Aşağıdaki örnek üzerinden inceleyelim. App.js içerisinden Button isimli bir bileşen çağrılır.

Çağrılan <Button /> bileşenin içeriğini aşağıda görebilirsiniz. Eğer bu bileşen dışa aktarılırken React.memo() kullanılmasaydı, App.js’deki her değişiklikte yeniden render edilecekti. Ancak React.memo() içerisine alındığı için sadece {title} propu değiştiğinde render edilecek.

Özetle; React.memo bir bileşenin proplarının değişmesine bağlı olarak, bileşenin yeniden render edilip edilmemesi gerektiğine karar verir ve böylece bileşenlerin gereksiz render edilmesi önlenmiş olur.

useMemo

Bir React Hook olan useMemo, maliyetli fonksiyonların gereksiz yere tekrardan çalıştırılmasını önlemek için kullanılır. Maliyetli fonksiyonlardan kasıt; uzun ve işlem gücünü belirgin bir şekilde meşgul eden fonksiyonlardır. Basit fonksiyonlarda useMemo kullanılması gereksiz ve hatta zararlıdır.

Aşağıdaki resimde de göründüğü gibi useMemo iki parametre alır. Birincisi; ilgili fonksiyondur, ikincisi ise o fonksiyonun yeniden çalıştırılması için gereken bağımlılıklardır.

Aşağıdaki örnek useMemo kullanımı için iyi bir örnektir. İnceledikten sonra açıklamasını okuyabilirsiniz.

  • App bileşeni içerisinde hem bir to-do list hem de bir counter (sayaç) uygulaması yapılmıştır.
  • To-do’ya bir eleman eklendiğinde bileşenin yeniden render edilmesi ve sayaç ile alakalı olan expensiveCalculation isimli fonksiyonun da gereksiz yere çalıştırılması gerekecekti.
  • Ancak useMemo kullanıldı ve bağımlılık olarak count değeri parametre olarak verildi.
  • Bu sayede, sadece count değeri değiştiğinde expensiveCalculation fonksiyonu yeniden hesaplanacaktır.
  • Count değeri değişmediği sürece, hafızada tutulan fonksiyon sonucu kullanılacaktır.

Özetle, useMemo maliyetli fonksiyonların gereksiz yere render edilmemesi için fonksiyon sonucunun hafızada tutulması ve bağımlı olduğu değişken değişmedikçe hafızada tutulan değeri döndüren bir optimizasyon hookudur.

useCallback

useCallback, tıpkı useMemo gibi fonksiyonların optimizasyonu için kullanıldığı için birbirine çok benzerdir ve bazen karıştırılsalar da farklı amaçlara hizmet ederler.

Ayrıntılara girmeden önce aralarındaki farkı bir tanım ile açıklayacak olursak; useMemo fonksiyonların döndürdüğü değeri hafızasında tutar. useCallback ise fonksiyonları hafızasında tutar.

React’ın resmi sayfasında verilen aşağıdaki örnek, useCallback’in, useMemo’yu kapsayan bir hook olduğunu ayrıca göstermektedir.

Nasıl çalıştığını yine örnek üzerinden anlatmak daha doğru olacaktır. Aşağıdaki App (parent) ve Todos(child) bileşenleri inceledikten sonra açıklamayı okuyabilirsiniz.

  • App içerisinde yine bir to-do list ve bir sayaç uygulaması yapılmıştır.
  • Todos ise React.memo ile döndürülmüştür ki propları değişmediği sürece yeniden render edilmesin.
  • Ancak sayaç butonuna basıldığında ve Todos’un propları değişmediği halde yeniden render edilir. Ve kontrol amaçlı Todos bileşenine eklediğimiz console.log() çalışır.
  • Bunun sebebi; JavaScript’de object veri türündeki değerlerin bellekte tuttuğu adres açısından farklıymış gibi algılanmasıdır. Aşağıdaki görselde string ve number değişkenlerin birbirine eşit olduğu ancak array, object ve function değişkenlerinin birbirine eşit olmadığı görülecektir.
  • Yani prop olarak bir fonksiyon gönderdiğimiz için her defasında aynı fonksiyonu kullanmamıza rağmen, prop değişmiş olarak algılanır ve alt bileşendeki React.memo’ya rağmen yeniden render edilir.
  • Tekrar App bileşenine bakacak olursanız, yorum satırına alınmış olan useCallback aktif hale getirilseydi ve diğer fonksiyon silinseydi. Sayaç değiştiğinde alt bileşen yeniden render edilmeyecekti. Çünkü useCallback ile fonksiyonumuzun adresini hafızaya kaydettik ve propun değişmediği bir durum yarattık.

Özetle, useCallback fonksiyonun döndürdüğü değeri değil, fonksiyonun adresini hafızasında tutar. Bu sayede prop olarak gönderilen fonksiyon değişmiş olarak algılanmaz.

useCallback ve useMemo’nun her ikisinin de fonksiyonlar için kullanıldığını öğrendik. Aralarındaki farkı hızlı bir şekilde düşünebilmek için şöyle bir genelleme yapabiliriz; fonksiyon bir eylem içeriyorsa ve değer döndürmüyorsa useCallback, bir değer döndürüyorsa useMemo kullanılması muhtemeldir.

Hangi durumlarda kullanılmalıdır

Bazı çalışma ekipleri, useCallback gibi optimizasyon araçlarını bir standart olarak tüm fonksiyonlara eklerler ancak bu kötü bir uygulamadır. Çünkü, kodun bakımını zorlaştırır, okunabilirliğini bozar ve hafızada saklanan veri miktarını gereksiz yere artırır. Bu sebeple gerekli olduğu düşünüldüğü durumlarda kullanılmalıdır.

Bonus

Bileşenlerin kaç defa render edildiğini takip etmek için bileşen içerisine console.log() eklemek mümkündür ancak bu, büyük projelerde vakit kaybına sebep olur.

Bunun yerine, bir Chrome eklentisi olan React Developer Tools kullanılabilir. Eklentiyi yükledikten sonra Chrome DevTools’a eklentiye ait sekmeler eklenir. Bu sekmelerden Profiler isimli sekme üzerinden uygulamanın işleyişi ve render sayısı ile ilgili bilgilere erişebilirsiniz.

Tüm yazılarımın kategorize edilmiş bir listesine aşağıdaki linkten erişebilirsiniz.
https://github.com/oguzhanuyanik-sr/articles

--

--