React Compiler v1.0: useMemo ve useCallback Yazmadan Otomatik Memoization
React ekosisteminde yıllardır bir gerçekle yüz yüzeyiz: Performanslı uygulamalar yazmak istiyorsanız useMemo, useCallback ve React.memo'yu doğru yerlerde, doğru şekilde kullanmanız gerekiyor. Ancak bu hook'ların ne zaman gerekli olduğunu bilmek, dependency array'leri doğru yönetmek ve gereksiz memoization'dan kaçınmak bile başlı başına bir uzmanlık alanı haline geldi.
React Compiler v1.0 (eski adıyla React Forget) ile bu tablo kökten değişiyor. Meta'nın yıllarca üzerinde çalıştığı bu derleyici, bileşenlerinizi otomatik olarak memoize ederek siz hiçbir şey yazmadan performans optimizasyonlarını gerçekleştiriyor.
Mevcut Durum: Manuel Memoization'ın Sorunları
React Compiler'ın neden bu kadar önemli olduğunu anlamak için önce mevcut duruma bakalım. Bugün tipik bir React bileşeninde performans optimizasyonu şöyle görünüyor:
import { useMemo, useCallback, memo } from 'react';
const ExpensiveList = memo(({ items, onItemClick, filter }) => {
const filteredItems = useMemo(() => {
return items.filter(item => item.category === filter);
}, [items, filter]);
const handleClick = useCallback((id) => {
onItemClick(id);
}, [onItemClick]);
const stats = useMemo(() => {
return {
total: filteredItems.length,
avgPrice: filteredItems.reduce((sum, item) => sum + item.price, 0) / filteredItems.length,
};
}, [filteredItems]);
return (
<div>
<Stats data={stats} />
{filteredItems.map(item => (
<ListItem key={item.id} item={item} onClick={handleClick} />
))}
</div>
);
});Bu kodda üç farklı memoization tekniği kullandık. Peki bu yaklaşımın sorunları neler?
1. Bilişsel Yük
Geliştiriciler, iş mantığına odaklanmak yerine "bu değeri memoize etmeli miyim?" sorusuyla sürekli meşgul oluyor. Her yeni state veya prop eklendiğinde dependency array'lerin güncellenmesi gerekiyor.
2. Hata Riski
Eksik veya fazla dependency, stale closure'lar ve gereksiz memoization gibi hatalar çok yaygın. ESLint kuralları yardımcı olsa da her durumu yakalayamıyor.
3. Kod Kirliliği
Gerçek iş mantığı, optimizasyon kodlarının arasında kaybolabiliyor. Bir bileşenin %30-40'ı salt performans kodu olabiliyor.
4. Tutarsız Uygulama
Takım içinde herkes aynı seviyede memoization bilgisine sahip değil. Birisi her yeri sararken, diğeri hiç kullanmıyor.
React Compiler Nedir ve Nasıl Çalışır?
React Compiler, build zamanında çalışan bir derleyicidir. Babel veya SWC pipeline'ınıza entegre olur ve React bileşenlerinizi analiz ederek otomatik memoization kodu üretir. Çalışma zamanında (runtime) ek bir maliyet yoktur — tüm optimizasyonlar derleme aşamasında gerçekleşir.
Temel Çalışma Prensibi
Compiler, bileşenlerinizi analiz ederken şu adımları izler:
- Statik Analiz: Bileşenin JSX yapısını, kullanılan değişkenleri ve bağımlılıkları analiz eder.
- Reaktivite Grafiği: Hangi değerlerin hangi state/prop'lara bağlı olduğunu belirler.
- Otomatik Memoization: Gereksiz yeniden hesaplamaları engellemek için uygun yerlere memoization kodu ekler.
- Kod Üretimi: Optimize edilmiş kodu çıktı olarak üretir.
Öncesi ve Sonrası
Aynı bileşeni React Compiler ile yazarsanız:
// React Compiler ile — siz sadece bunu yazıyorsunuz:
const ExpensiveList = ({ items, onItemClick, filter }) => {
const filteredItems = items.filter(item => item.category === filter);
const handleClick = (id) => {
onItemClick(id);
};
const stats = {
total: filteredItems.length,
avgPrice: filteredItems.reduce((sum, item) => sum + item.price, 0) / filteredItems.length,
};
return (
<div>
<Stats data={stats} />
{filteredItems.map(item => (
<ListItem key={item.id} item={item} onClick={handleClick} />
))}
</div>
);
};Kodunuz tertemiz, hiçbir optimizasyon detayı yok. Ancak derleme sonrasında React Compiler, filteredItems'ı sadece items veya filter değiştiğinde yeniden hesaplayacak, handleClick'i sadece onItemClick değiştiğinde yeniden oluşturacak ve stats objesini sadece filteredItems değiştiğinde yeniden yaratacak şekilde otomatik memoization uygular.
React Compiler'ı Projenize Entegre Etme
Kurulum
React Compiler'ı projenize eklemek oldukça kolaydır:
npm install -D react-compiler-runtime
npm install -D babel-plugin-react-compilerBabel Konfigürasyonu
babel.config.js dosyanıza plugin'i ekleyin:
// babel.config.js
module.exports = {
plugins: [
['babel-plugin-react-compiler', {
// Compiler'ın çalışma modunu belirler
runtimeModule: 'react-compiler-runtime',
}],
],
};Vite ile Kullanım
Vite projelerinde entegrasyon şöyle yapılır:
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
react({
babel: {
plugins: [
['babel-plugin-react-compiler', {}],
],
},
}),
],
});Next.js ile Kullanım
Next.js 15+ sürümlerinde React Compiler desteği yerleşik olarak gelir:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
module.exports = nextConfig;React Compiler'ın Kuralları: Rules of React
React Compiler'ın doğru çalışabilmesi için kodunuzun React'in kurallarına uyması gerekir. Bu kurallar aslında zaten var olan ve uyulması gereken kurallardır, ancak şimdi Compiler bunları zorunlu kılıyor.
Temel Kurallar
// ❌ YANLIŞ: Render sırasında dış state'i mutasyona uğratmak
let globalCounter = 0;
function BadComponent() {
globalCounter++; // Compiler bu kodu optimize edemez!
return <div>{globalCounter}</div>;
}
// ✅ DOĞRU: State yönetimini React'e bırakmak
function GoodComponent() {
const [counter, setCounter] = useState(0);
return (
<div>
<span>{counter}</span>
<button onClick={() => setCounter(c => c + 1)}>Artır</button>
</div>
);
}// ❌ YANLIŞ: Props'u doğrudan mutasyona uğratmak
function BadList({ items }) {
items.sort((a, b) => a.name.localeCompare(b.name)); // Props mutasyonu!
return items.map(item => <div key={item.id}>{item.name}</div>);
}
// ✅ DOĞRU: Yeni bir dizi oluşturmak
function GoodList({ items }) {
const sortedItems = [...items].sort((a, b) => a.name.localeCompare(b.name));
return sortedItems.map(item => <div key={item.id}>{item.name}</div>);
}Hook Kuralları
Hook'lar her zaman bileşenin en üst seviyesinde, koşulsuz olarak çağrılmalıdır:
// ❌ YANLIŞ: Koşullu hook kullanımı
function ConditionalComponent({ isLoggedIn }) {
if (isLoggedIn) {
const [user, setUser] = useState(null); // Koşul içinde hook!
}
return <div>...</div>;
}
// ✅ DOĞRU: Hook'u her zaman çağır, değeri koşullu kullan
function CorrectComponent({ isLoggedIn }) {
const [user, setUser] = useState(null);
return (
<div>
{isLoggedIn && user && <span>{user.name}</span>}
</div>
);
}Gerçek Dünya Senaryoları
Senaryo 1: Form Bileşenleri
Form bileşenleri, gereksiz render'ların en sık yaşandığı yerlerden biridir. React Compiler ile:
function ProductForm({ categories, onSubmit }) {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [category, setCategory] = useState('');
// Compiler bu hesaplamayı sadece categories değiştiğinde yapar
const categoryOptions = categories.map(cat => ({
value: cat.id,
label: cat.name,
}));
// Compiler bu fonksiyonu otomatik olarak memoize eder
const handleSubmit = (e) => {
e.preventDefault();
onSubmit({ name, price: Number(price), category });
};
// Compiler bu validasyonu sadece name ve price değiştiğinde çalıştırır
const isValid = name.trim().length > 0 && Number(price) > 0;
return (
<form onSubmit={handleSubmit}>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={price} onChange={e => setPrice(e.target.value)} />
<Select options={categoryOptions} value={category} onChange={setCategory} />
<button disabled={!isValid}>Kaydet</button>
</form>
);
}Senaryo 2: Dashboard Bileşenleri
Birden fazla veri kaynağını birleştiren dashboard bileşenlerinde:
function SalesDashboard({ sales, returns, dateRange }) {
// Compiler her hesaplamayı bağımlılıklarına göre ayrı ayrı memoize eder
const totalSales = sales.reduce((sum, s) => sum + s.amount, 0);
const totalReturns = returns.reduce((sum, r) => sum + r.amount, 0);
const netRevenue = totalSales - totalReturns;
const chartData = sales.map(s => ({
date: s.date,
amount: s.amount,
}));
const topProducts = sales
.reduce((acc, sale) => {
const existing = acc.find(p => p.id === sale.productId);
if (existing) existing.total += sale.amount;
else acc.push({ id: sale.productId, name: sale.productName, total: sale.amount });
return acc;
}, [])
.sort((a, b) => b.total - a.total)
.slice(0, 5);
return (
<div>
<MetricCard title="Toplam Satış" value={totalSales} />
<MetricCard title="İadeler" value={totalReturns} />
<MetricCard title="Net Gelir" value={netRevenue} />
<SalesChart data={chartData} range={dateRange} />
<TopProductsList products={topProducts} />
</div>
);
}Bu bileşende, sales değişmeden returns değişirse, chartData ve topProducts yeniden hesaplanmaz. Compiler bunu tamamen otomatik olarak yapar.
ESLint Plugin ile Uyumluluk Kontrolü
React Compiler ile birlikte gelen ESLint plugin'i, kodunuzun Compiler ile uyumlu olup olmadığını kontrol eder:
npm install -D eslint-plugin-react-compiler// eslint.config.js
import reactCompiler from 'eslint-plugin-react-compiler';
export default [
{
plugins: {
'react-compiler': reactCompiler,
},
rules: {
'react-compiler/react-compiler': 'error',
},
},
];Bu plugin, Compiler'ın optimize edemeyeceği kod kalıplarını size bildirir ve düzeltme önerileri sunar.
Belirli Bileşenleri Hariç Tutma
Eğer bir bileşenin Compiler tarafından optimize edilmesini istemiyorsanız, "use no memo" direktifini kullanabilirsiniz:
function LegacyComponent({ data }) {
"use no memo"; // Bu bileşen Compiler tarafından optimize edilmez
// Eski, kurallara uymayan kod...
return <div>{data.value}</div>;
}Bu, özellikle kademeli geçiş sürecinde çok faydalıdır. Tüm projeyi bir anda taşımak yerine, sorunlu bileşenleri geçici olarak hariç tutabilir ve zamanla düzeltebilirsiniz.
Performans Karşılaştırması
React Compiler'ın sağladığı performans iyileştirmeleri, uygulamanın karmaşıklığına göre değişir. Meta'nın Instagram web uygulamasındaki testlerde şu sonuçlar elde edilmiştir:
| Metrik | Manuel Memoization | React Compiler |
|---|---|---|
| Gereksiz re-render | Geliştiriciye bağlı | ~%95 azalma |
| Bundle boyutu etkisi | Hook import'ları | Minimal artış |
| Geliştirici verimliliği | Düşük | Yüksek |
| Hata oranı (stale closure vb.) | Orta-Yüksek | Neredeyse sıfır |
Mevcut useMemo/useCallback Kullanımları Ne Olacak?
Mevcut kodunuzdaki useMemo, useCallback ve React.memo kullanımları çalışmaya devam edecek. React Compiler bu hook'ları görünce zaten memoize edilmiş olduğunu anlar ve üzerine ek bir şey yapmaz. Yani geriye dönük uyumluluk tamamen korunur.
Ancak zaman içinde bu hook'ları kaldırarak kodunuzu sadeleştirebilirsiniz:
// Eski kod — çalışmaya devam eder
const memoizedValue = useMemo(() => expensiveCalculation(a, b), [a, b]);
// Yeni, temiz kod — Compiler otomatik optimize eder
const memoizedValue = expensiveCalculation(a, b);Sıkça Sorulan Sorular
Compiler tüm bileşenleri memoize eder mi? Hayır, Compiler sadece optimize etmenin fayda sağlayacağı yerleri belirler. Zaten basit ve hızlı olan hesaplamaları gereksiz yere memoize etmez.
Server Components ile uyumlu mu? React Compiler yalnızca client components üzerinde çalışır. Server Components zaten sunucuda render edildiği için memoization'a ihtiyaç duymaz.
TypeScript ile çalışır mı? Evet, React Compiler TypeScript ile tam uyumludur. Tip bilgilerini de optimizasyon kararlarında kullanır.
Sonuç
React Compiler v1.0, React ekosisteminde bir paradigma değişimini temsil ediyor. Yıllardır geliştiricilerin omuzlarına yüklenen performans optimizasyonu sorumluluğu artık derleyiciye devrediliyor.
Önemli çıkarımlar:
- useMemo, useCallback ve React.memo artık manuel olarak yazılması gerekmiyor. Compiler bunları sizin yerinize, daha doğru ve tutarlı bir şekilde uyguluyor.
- Kodunuz daha temiz, okunabilir ve bakımı kolay hale geliyor. İş mantığı ile optimizasyon kodu birbirinden ayrılıyor.
- Geçiş kademeli olabilir. Mevcut memoization kodunuz çalışmaya devam eder, yeni kod yazarken hook'ları atlayabilirsiniz.
- React'in kurallarına uymak artık her zamankinden daha önemli. Compiler, bu kuralları ihlal eden kodları optimize edemez.
React Compiler, performanslı uygulamalar yazmanın önündeki en büyük engellerden birini kaldırarak geliştiricilerin asıl odaklanması gereken şeye — kullanıcı deneyimine — daha fazla zaman ayırmasını sağlıyor. Eğer henüz denemediyseniz, bugün bir yan projede başlayarak Compiler'ın farkını bizzat görmenizi şiddetle tavsiye ederim.