React.memo ve Derin Karşılaştırma Stratejileri

React.memo ve Derin Karşılaştırma Stratejileri
React uygulamalarında performans optimizasyonu kritik bir konudur. React.memo, fonksiyonel bileşenleri memoize ederek gereksiz yeniden render'ları önler. Ancak doğru kullanılmadığında beklenen faydayı sağlamayabilir. Bu yazıda React.memo'nun çalışma prensibini, derin karşılaştırma stratejilerini ve en iyi uygulamaları detaylı olarak inceleyeceğiz.
React.memo Nedir?
React.memo, bir Higher-Order Component (HOC) olup fonksiyonel bileşenleri sarmalayarak prop'lar değişmediğinde yeniden render'ı engeller. Varsayılan olarak sığ (shallow) karşılaştırma yapar.
import { memo } from 'react';
// Temel kullanım
const UserCard = memo(function UserCard({ name, email, avatar }) {
console.log('UserCard render edildi');
return (
<div className="user-card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
<p>{email}</p>
</div>
);
});
// Parent bileşen her render olduğunda,
// UserCard'ın prop'ları değişmediyse tekrar render edilmez
function UserList() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
{/* count değişse bile UserCard tekrar render edilmez */}
<UserCard name="Ahmet" email="ahmet@mail.com" avatar="/avatar.jpg" />
</div>
);
}
Shallow (Sığ) Karşılaştırma Nasıl Çalışır?
React.memo varsayılan olarak Object.is ile sığ karşılaştırma yapar. Bu, her prop'un referans eşitliğini kontrol eder:
// Object.is karşılaştırma örnekleri
Object.is('hello', 'hello'); // true — string eşitliği
Object.is(42, 42); // true — sayı eşitliği
Object.is(true, true); // true — boolean eşitliği
// Referans tipleri — her seferinde yeni referans
Object.is({ a: 1 }, { a: 1 }); // false! — farklı referanslar
Object.is([1, 2], [1, 2]); // false! — farklı referanslar
// Fonksiyonlar
const fn1 = () => {};
const fn2 = () => {};
Object.is(fn1, fn2); // false! — farklı referanslar
Object.is(fn1, fn1); // true — aynı referans
useMemo ve useCallback kullanılmalıdır.
Referans Stabilitesi Sağlama
import { memo, useMemo, useCallback } from 'react';
const DataTable = memo(function DataTable({ data, columns, onRowClick }) {
return (
<table>
<thead>
<tr>
{columns.map((col) => (
<th key={col.key}>{col.label}</th>
))}
</tr>
</thead>
<tbody>
{data.map((row) => (
<tr key={row.id} onClick={() => onRowClick(row)}>
{columns.map((col) => (
<td key={col.key}>{row[col.key]}</td>
))}
</tr>
))}
</tbody>
</table>
);
});
function Dashboard() {
const [filter, setFilter] = useState('');
const [selectedRow, setSelectedRow] = useState(null);
// useMemo ile obje/dizi referansını stabilize et
const columns = useMemo(() => [
{ key: 'name', label: 'İsim' },
{ key: 'email', label: 'E-posta' },
{ key: 'role', label: 'Rol' },
], []);
const filteredData = useMemo(() =>
users.filter((u) => u.name.includes(filter)),
[filter]
);
// useCallback ile fonksiyon referansını stabilize et
const handleRowClick = useCallback((row) => {
setSelectedRow(row);
}, []);
return (
<div>
<input value={filter} onChange={(e) => setFilter(e.target.value)} />
<DataTable
data={filteredData}
columns={columns}
onRowClick={handleRowClick}
/>
</div>
);
}
Özel Karşılaştırma Fonksiyonu
React.memo'ya ikinci parametre olarak özel bir karşılaştırma fonksiyonu geçebilirsiniz. Bu fonksiyon, derin karşılaştırma veya belirli prop'ları görmezden gelme gibi senaryolarda kullanılır.
const Chart = memo(
function Chart({ data, config, onHover }) {
// Ağır render işlemi
return <canvas ref={canvasRef} />;
},
(prevProps, nextProps) => {
// true döndürmek = render'ı atla (prop'lar eşit)
// false döndürmek = tekrar render et (prop'lar farklı)
// Sadece data ve config'i karşılaştır, onHover'ı görmezden gel
return (
prevProps.data === nextProps.data &&
prevProps.config.type === nextProps.config.type &&
prevProps.config.theme === nextProps.config.theme
);
}
);
// Derin karşılaştırma örneği
import isEqual from 'lodash/isEqual';
const DeepComparedList = memo(
function DeepComparedList({ items, options }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
},
(prevProps, nextProps) => {
return isEqual(prevProps, nextProps);
}
);
Ne Zaman React.memo Kullanmalı?
| Kullanın | Kullanmayın |
|---|---|
| Ağır render işlemleri olan bileşenler | Zaten hızlı render edilen küçük bileşenler |
| Aynı prop'larla sık render edilen bileşenler | Her render'da farklı prop alan bileşenler |
| Büyük listelerde her bir liste öğesi | Primitive prop alan basit bileşenler |
| Üst bileşen sık render olduğunda | Children prop kullanan wrapper bileşenler |
React Compiler ve Otomatik Memoization
React 19 ile birlikte gelen React Compiler, bileşenleri ve hook'ları otomatik olarak memoize eder. Bu, gelecekte React.memo, useMemo ve useCallback kullanımına olan ihtiyacı büyük ölçüde azaltacaktır.
// React Compiler öncesi — manuel memoization
const MemoizedList = memo(function List({ items }) {
const sorted = useMemo(() => items.sort(), [items]);
const handleClick = useCallback((id) => {
console.log(id);
}, []);
return sorted.map(item => <Item key={item.id} onClick={handleClick} />);
});
// React Compiler sonrası — otomatik memoization
// Compiler aynı optimizasyonları otomatik uygular
function List({ items }) {
const sorted = items.sort();
const handleClick = (id) => console.log(id);
return sorted.map(item => <Item key={item.id} onClick={handleClick} />);
}
Performans Profiling
React.memo'nun gerçekten fayda sağladığını doğrulamak için React DevTools Profiler kullanın:
// React DevTools Profiler ile ölçüm
import { Profiler } from 'react';
function onRenderCallback(id, phase, actualDuration) {
console.log(`${id} — ${phase} — ${actualDuration}ms`);
}
function App() {
return (
<Profiler id="DataTable" onRender={onRenderCallback}>
<DataTable data={data} />
</Profiler>
);
}
Anti-Pattern'ler
// Anti-pattern 1: Her render'da yeni obje oluşturma
function Parent() {
return (
<MemoizedChild
style={{ color: 'red' }} // Her render'da yeni referans!
data={[1, 2, 3]} // Her render'da yeni dizi!
/>
);
}
// Anti-pattern 2: Inline fonksiyon
function Parent() {
return (
<MemoizedChild
onClick={() => console.log('click')} // Her render'da yeni fonksiyon!
/>
);
}
// Anti-pattern 3: Children geçirme
function Parent() {
return (
<MemoizedWrapper>
<span>İçerik</span> {/* Her render'da yeni JSX! */}
</MemoizedWrapper>
);
}
useState) veya context'ini (useContext) kullanıyorsa, bunlar değiştiğinde memo'dan bağımsız olarak yeniden render edilir.
Sonuç
React.memo, doğru kullanıldığında performans kazancı sağlayan güçlü bir optimizasyon aracıdır. Sığ karşılaştırmanın sınırlamalarını anlamak, referans stabilitesini useMemo ve useCallback ile sağlamak ve gerektiğinde özel karşılaştırma fonksiyonları yazmak başarılı kullanımın anahtarıdır. Her bileşeni memo ile sarmak yerine, önce React DevTools Profiler ile darboğazları tespit edin ve sadece ihtiyaç duyulan yerlerde optimize edin. React Compiler'ın yaygınlaşmasıyla manuel memoization ihtiyacı azalacak olsa da, bu kavramları anlamak her React geliştiricisi için değerlidir.