Merhaba arkadaşlar, bugün sizlerle React'te performans optimizasyonu yaparken sıkça karşımıza çıkan, bazen de gereksiz yere kullanıp "acaba doğru mu yapıyorum?" dedirten bir konuyu, React.memo'yu konuşacağız. Özellikle büyük uygulamalarda, bir component'in gereksiz yere render olması, uygulamanın yavaşlamasına ve kullanıcı deneyiminin bozulmasına neden olabiliyor. Bu hatayı ilk gördüğümde, "State'im aynı, neden yeniden render oluyor bu?" diye kafayı yemiştim. İşte benim bu süreçte öğrendiğim, React.memo'yu ne zaman kullanıp ne zaman kullanmamamız gerektiği.
Karşılaştığım Sorun
Bir projede, ana sayfada bir filtreleme paneli ve bu filtreye bağlı yüzlerce ürünü listeleyen bir component vardı. Filtre state'i değiştiğinde, mantıken sadece ürün listesi component'i render olmalıydı. Ancak React Dev Tools'un "Highlight updates when components render" özelliğini açtığımda gördüm ki, filtre panelindeki her bir buton, input bileşeni de yeniden render oluyordu! Bu, props'ları hiç değişmeyen, saf görünümlü bileşenler için büyük bir performans kaybıydı.
React.memo Nedir ve Nasıl Çalışır?
React.memo, bir Higher Order Component (HOC)'tir. Bir fonksiyonel component'i alır, onu hafızaya (memoization) alınmış yeni bir component olarak döndürür. Bu yeni component, kendisine gelen props'ları bir önceki render ile karşılaştırır. Eğer props'lar shallow comparison (sığ karşılaştırma) ile aynıysa, React bir önceki render sonucunu tekrar kullanır ve component'i yeniden render etmez.
Temel kullanımı şöyle:
React.memo'yu KULLANMAN Gereken Durumlar
1. Props'ları Sık Değişmeyen Saf Bileşenler: Bir component, aynı props ile defalarca render ediliyorsa (örneğin, bir sidebar item'ı, bir başlık, bir statik kart) ideal adaydır.
2. Render'ı Gerçekten Pahalı Bileşenler: İçinde ağır hesaplamalar, harici veri işlemleri veya çok fazla alt bileşen (büyük liste) olan component'ler. Burada memo, render maliyetinden daha ucuz kalır.
3. Üst Bileşen Sık Render Oluyorsa: Üst component (parent) sık sık state değişikliği yaşıyor ama alt component'e (child) ilettiği props değişmiyorsa, alt component'i memo ile sarmalamak çok faydalıdır.
React.memo'yu KULLANMAMAN Gereken Duramlar
1. Component Zaten Basit ve Hafifse: Eğer component'in kendisi çok basitse (bir buton, bir ikon), memo'nun props karşılaştırma maliyeti, yeniden render maliyetinden daha yüksek olabilir. Premature optimization yapmaktan kaçının.
2. Props'ları Neredeyse Her Zaman Değişiyorsa: Eğer component her seferinde farklı props alıyorsa, memo hiçbir işe yaramaz çünkü her seferinde render edilmesi gerekecektir. Hatta karşılaştırma için ekstra bir maliyet getirir.
3. Props Olarak Fonksiyon veya Nesne Alıyorsa (Dikkat!): Bu en sık düşülen tuzak! React'te fonksiyonlar ve nesneler referans tiptedir. Üst component her render olduğunda, onClickHandler gibi bir fonksiyon prop'u yeni bir referans ile oluşturulur. Bu da memo'nun "props'lar değişti" demesine ve bileşeni yeniden render etmesine neden olur. Çözüm: useCallback veya useMemo.
useCallback ile Birlikte Kullanım (Kritik Kombinasyon)
İşte benim kullandığım en temiz çözüm. Eğer memo'lu bir componente fonksiyon geçiriyorsanız, o fonksiyonu mutlaka `useCallback` ile sarmalayın.
Sonuç olarak, React.memo güçlü bir optimizasyon aracıdır ama her bileşene uygulanacak sihirli bir değnek değildir. Kullanmadan önce "Bu bileşen gerçekten pahalı mı render oluyor?" ve "Props'ları gerçekten stabil kalabiliyor mu?" sorularını kendinize sorun. Profiler aracını kullanarak ölçüm yapmak da her zaman en doğru yoldur.
Siz React'te performans için hangi yöntemleri kullanıyorsunuz? `useMemo` ile `React.memo` arasındaki farkları nasıl değerlendiriyorsunuz? Ya da memo kullanırken sizi en çok hangi tuzaklar zor durumda bıraktı? Yorumlarda tartışalım!
Bir projede, ana sayfada bir filtreleme paneli ve bu filtreye bağlı yüzlerce ürünü listeleyen bir component vardı. Filtre state'i değiştiğinde, mantıken sadece ürün listesi component'i render olmalıydı. Ancak React Dev Tools'un "Highlight updates when components render" özelliğini açtığımda gördüm ki, filtre panelindeki her bir buton, input bileşeni de yeniden render oluyordu! Bu, props'ları hiç değişmeyen, saf görünümlü bileşenler için büyük bir performans kaybıydı.
React.memo, bir Higher Order Component (HOC)'tir. Bir fonksiyonel component'i alır, onu hafızaya (memoization) alınmış yeni bir component olarak döndürür. Bu yeni component, kendisine gelen props'ları bir önceki render ile karşılaştırır. Eğer props'lar shallow comparison (sığ karşılaştırma) ile aynıysa, React bir önceki render sonucunu tekrar kullanır ve component'i yeniden render etmez.
Temel kullanımı şöyle:
JavaScript:
import React from 'react';
const MyExpensiveComponent = ({ title, items }) => {
// Ağır hesaplamalar yapan bir component
console.log('MyExpensiveComponent rendered!');
return (
<div>
<h2>{title}</h2>
<ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>
</div>
);
};
export default React.memo(MyExpensiveComponent);
1. Props'ları Sık Değişmeyen Saf Bileşenler: Bir component, aynı props ile defalarca render ediliyorsa (örneğin, bir sidebar item'ı, bir başlık, bir statik kart) ideal adaydır.
2. Render'ı Gerçekten Pahalı Bileşenler: İçinde ağır hesaplamalar, harici veri işlemleri veya çok fazla alt bileşen (büyük liste) olan component'ler. Burada memo, render maliyetinden daha ucuz kalır.
3. Üst Bileşen Sık Render Oluyorsa: Üst component (parent) sık sık state değişikliği yaşıyor ama alt component'e (child) ilettiği props değişmiyorsa, alt component'i memo ile sarmalamak çok faydalıdır.
1. Component Zaten Basit ve Hafifse: Eğer component'in kendisi çok basitse (bir buton, bir ikon), memo'nun props karşılaştırma maliyeti, yeniden render maliyetinden daha yüksek olabilir. Premature optimization yapmaktan kaçının.
2. Props'ları Neredeyse Her Zaman Değişiyorsa: Eğer component her seferinde farklı props alıyorsa, memo hiçbir işe yaramaz çünkü her seferinde render edilmesi gerekecektir. Hatta karşılaştırma için ekstra bir maliyet getirir.
3. Props Olarak Fonksiyon veya Nesne Alıyorsa (Dikkat!): Bu en sık düşülen tuzak! React'te fonksiyonlar ve nesneler referans tiptedir. Üst component her render olduğunda, onClickHandler gibi bir fonksiyon prop'u yeni bir referans ile oluşturulur. Bu da memo'nun "props'lar değişti" demesine ve bileşeni yeniden render etmesine neden olur. Çözüm: useCallback veya useMemo.
İşte benim kullandığım en temiz çözüm. Eğer memo'lu bir componente fonksiyon geçiriyorsanız, o fonksiyonu mutlaka `useCallback` ile sarmalayın.
JavaScript:
import React, { useState, useCallback } from 'react';
import MemoizedChild from './MemoizedChild';
const ParentComponent = () => {
const [count, setCount] = useState(0);
const [filter, setFilter] = useState('');
// Bu fonksiyon, count değişmediği sürece aynı referansı korur.
const handleReset = useCallback(() => {
setFilter('');
}, []); // Bağımlılık dizisi boş, çünkü hiçbir state/prop'a bağlı değil.
// Bu fonksiyon, filter değiştiğinde yeniden oluşturulur.
const handleFilterChange = useCallback((newFilter) => {
setFilter(newFilter);
}, []);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Arttır: {count}</button>
{/ MemoizedChild, handleReset prop'u değişmediği için count artışından ETKİLENMEZ /}
<MemoizedChild onReset={handleReset} filterValue={filter} onFilterChange={handleFilterChange} />
</div>
);
};
Sonuç olarak, React.memo güçlü bir optimizasyon aracıdır ama her bileşene uygulanacak sihirli bir değnek değildir. Kullanmadan önce "Bu bileşen gerçekten pahalı mı render oluyor?" ve "Props'ları gerçekten stabil kalabiliyor mu?" sorularını kendinize sorun. Profiler aracını kullanarak ölçüm yapmak da her zaman en doğru yoldur.
Siz React'te performans için hangi yöntemleri kullanıyorsunuz? `useMemo` ile `React.memo` arasındaki farkları nasıl değerlendiriyorsunuz? Ya da memo kullanırken sizi en çok hangi tuzaklar zor durumda bıraktı? Yorumlarda tartışalım!