React Performans Optimizasyonuna Giriş
React uygulamaları, özellikle çok sayıda bileşene sahip karmaşık olanlar, bazen performans darboğazları yaşayabilir. Gereksiz re-render’lar yaygın bir sorundur ve yavaş kullanıcı arayüzlerine (UI) ve ideal olmayan bir kullanıcı deneyimine yol açar. Web geliştirme ve mobil çözümlerde lider bir ajans olan SoftCrafter olarak, yüksek performanslı uygulamaların kritik önemini anlıyoruz. Bu makale, bileşen render’larını optimize etmek ve uygulamanızın hızını artırmak için temel araçlar olan üç güçlü React özelliğini – memo, useMemo ve useCallback – derinlemesine inceliyor.
Bu teknikleri ne zaman ve nasıl uygulayacağınızı anlamak, verimli ve ölçeklenebilir React uygulamaları oluşturmanın anahtarıdır. React genellikle hızlı olsa da, geliştirdiğimiz e-ticaret platformları ve kurumsal hizmetler gibi büyük ölçekli projelerde küçük optimizasyonlar bile önemli bir fark yaratabilir.
React’in Re-rendering Mekanizmasını Anlamak
Optimizasyon tekniklerine geçmeden önce, React’in bileşenleri ne zaman yeniden render edeceğine nasıl karar verdiğini kısaca gözden geçirelim. Varsayılan olarak, bir parent component yeniden render edildiğinde, child component’lerinin prop’ları gerçekten değişmiş olsun ya da olmasın, tüm child component’leri de yeniden render edilir. Bu basamaklı re-render genellikle gereksizdir ve özellikle hesaplama açısından maliyetli bileşenler veya büyük listeler render edenler için önemli bir performans düşüşüne neden olabilir.
memo, useMemo ve useCallback‘in amacı, bileşenlerinize bir tür memoization veya caching eklemektir. Bu, React’in, bağımlılıkları son render’dan bu yana değişmediyse, bir bileşeni yeniden render etmeyi veya bir değeri/fonksiyonu yeniden hesaplamayı atlamasına olanak tanır. Bu seçici render, performansı optimize etmek için çok önemlidir.
React.memo: Gereksiz Bileşen Re-render’larını Önleme
React.memo, fonksiyonel bileşenleri sarmak için kullanılabilecek bir higher-order component (HOC)tir. React’e, prop’ları değişmediyse bileşeni render etmeyi atlamasını söyler. Prop’ların yüzeysel bir karşılaştırmasını yapar. Yeni prop’lar eski prop’larla aynıysa, React son render edilen sonucu yeniden kullanır.
React.memo ne zaman kullanılır:
- Bileşeniniz aynı prop’lar verildiğinde aynı çıktıyı veriyorsa.
- Bileşeniniz saf bir fonksiyonel bileşense (yan etkileri yoksa).
- Bileşenin render edilmesi maliyetliyse.
- Parent güncellemeleri nedeniyle sık sık yeniden render ediliyor, ancak kendi prop’ları nadiren değişiyorsa.
İşte bir örnek:
import React from 'react';
const ExpensiveComponent = ({ data }) => {
console.log('Rendering ExpensiveComponent');
// Imagine complex calculations or heavy UI rendering here
return <div>{data.map(item => <span key={item.id}>{item.name}</span>)}</div>;
};
export default React.memo(ExpensiveComponent);
Bu örnekte, ExpensiveComponent yalnızca data prop’u değişirse yeniden render edilecektir. Eğer parent’ı yeniden render edilir ancak aynı data prop’unu geçirirse, ExpensiveComponent yeniden render edilmeyecek ve değerli CPU döngülerinden tasarruf edilecektir.
useMemo: Maliyetli Hesaplamaları Memoize Etme
useMemo, bir fonksiyonun sonucunu memoize eden bir React Hook’udur. Bir fonksiyon ve bir bağımlılık dizisi alır. Yalnızca bağımlılıklardan biri değiştiğinde memoize edilmiş değeri yeniden hesaplar. Bu, maliyetli hesaplamaların her render’da çalışmasını önlemek için inanılmaz derecede faydalıdır.
useMemo ne zaman kullanılır:
- Hesaplama açısından yoğun bir görev gerçekleştiren bir fonksiyonunuz varsa.
- Bu fonksiyonun sonucu bileşeninizin render’ında kullanılıyorsa.
- Bu fonksiyonun girdileri (bağımlılıkları) sık sık değişmiyorsa.
Şu senaryoyu düşünün:
import React, { useState, useMemo } from 'react';
const ProductList = ({ products, filterText }) => {
const [count, setCount] = useState(0);
const filteredProducts = useMemo(() => {
console.log('Filtering products...');
return products.filter(product =>
product.name.toLowerCase().includes(filterText.toLowerCase())
);
}, [products, filterText]); // Dependencies
return (
<div>
<h2>Products</h2>
<button onClick={() => setCount(count + 1)}>Increment Count: {count}</button>
<ul>
{filteredProducts.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
};
Burada, filteredProducts yalnızca products veya filterText değişirse yeniden hesaplanacaktır. Eğer