Fri Aug 08 2025
Merhaba tekrar! React ile uygulama geliştirirken bir noktadan sonra hep aynı soruyla karşılaşırız: “Neden bu bileşen tekrar tekrar render oluyor?” İşte bu yazıda, tam da bu sorunun peşine düşüyoruz. Geliştirdiğim projelerde performans artışı elde etmek için sıkça başvurduğum iki güçlü React Hook’u var: useMemo ve useCallback. Bu araçlar, kulağa karmaşık gelse de doğru kullanıldığında uygulamanı gözle görülür şekilde hızlandırabilir.
Bugün seninle, bu iki hook’un ne işe yaradığını, neden gerektiğini ve günlük React kullanımında nasıl pratik faydalar sunduğunu konuşacağız. Bol örnekli, sade ve samimi bir rehber seni bekliyor. Hazırsan başlayalım!
_ useMemo, render'lar arasında hesaplanan bir değeri "hatırlamamızı" sağlar._
Bu tanım yüzeyde doğru olsa da, tam olarak neden buna ihtiyaç duyduğumuzu anlayabilmek için React’in nasıl çalıştığını biraz daha derinlemesine incelememiz gerekir.
React’in temel amacı, uygulamanın kullanıcı arayüzünü (UI), mevcut durum (state) ile senkronize tutmaktır. Bunu yaparken temel stratejisi şudur:
Her state değişikliğinde, React bileşenleri yeniden çalıştırır. Bu sürece “yeniden oluşturma” ya da daha teknik adıyla re-rendering denir.
Ve işin kritik kısmı şurası: React, önceki snapshot ile yenisini karşılaştırarak sadece gerçekten değişen kısımları DOM’a uygular. Bu da React'in verimli görünmesini sağlayan temel ilkedir.
React, kendi başına oldukça optimize bir sistemdir. Ancak bazı durumlarda bu yeniden oluşturma süreci pahalıya mal olabilir.
Örneğin:
gibi durumlar, özellikle büyük projelerde performans sorunlarına yol açabilir. Kullanıcı bir butona bastığında, ekranın geç tepki vermesi gibi senaryolar ortaya çıkabilir.
React'teki useMemo ve useCallback hook’ları, bu tür gereksiz iş yüklerini azaltmamıza yardımcı olur. İki temel optimizasyon sağlarlar:
Bu iki stratejiyi daha iyi anlamak için sıradaki bölümlerde örneklerle detaylıca inceleyeceğiz.
React uygulamaları oluştururken bazen çok zaman alan işlemlerle karşılaşırız. Bu işlemler her render olduğunda tekrarlanırsa, uygulamanın yavaşlamasına, donmasına hatta kullanıcının deneyiminin kötüleşmesine sebep olabilir.
Diyelim ki kullanıcıdan bir sayı alıyoruz ve bu sayıya kadar olan tüm Faktöriyel değerlerini hesaplıyoruz. Faktöriyel, bir sayının kendisi dahil olmak üzere, 1’e kadar olan tüm sayıların çarpımıdır.
Örnek:
5! = 5 × 4 × 3 × 2 × 1 = 120
3! = 3 × 2 × 1 = 6
Böyle bir işlem, özellikle sayılar büyüdükçe hesaplama süresini artırır.
import React from "react";
// Faktöriyel hesaplayan basit fonksiyon (recursive)
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
function App() {
const [num, setNum] = React.useState(10);
const [counter, setCounter] = React.useState(0);
// Seçilen sayıya kadar olan faktöriyel değerlerini hesaplayalım
const factorials = [];
for (let i = 1; i <= num; i++) {
factorials.push(factorial(i));
}
return (
<div>
<label>
Bir sayı girin:
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
/>
</label>
<p>
1'den {num}'a kadar faktöriyel değerleri:
<br />
{factorials.join(", ")}
</p>
<button onClick={() => setCounter(counter + 1)}>Sayaç: {counter}</button>
</div>
);
}
export default App;
Ne oluyor burada?
React’in çalışma şekli şöyledir:
Her render çağrıldığında, fonksiyon bileşeni baştan çalışır. Yukarıdaki kodda, factorial hesaplamaları da her render’da yeniden yapılır
Bu problem özellikle:
performans sorunlarına yol açabilir.
React, böyle maliyetli hesaplamaları sadece gerektiğinde tekrar yapmamızı kolaylaştırır: useMemo adındaki hook ile.
const factorials = React.useMemo(() => {
const results = [];
for (let i = 1; i <= num; i++) {
results.push(factorial(i));
}
return results;
}, [num]);
Yukarıdaki kodda
function App() {
const [num, setNum] = React.useState(10);
const [counter, setCounter] = React.useState(0);
const factorials = React.useMemo(() => {
const results = [];
for (let i = 1; i <= num; i++) {
results.push(factorial(i));
}
return results;
}, [num]);
return (
<div>
<label>
Bir sayı girin:
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
/>
</label>
<p>
1'den {num}'a kadar faktöriyel değerleri:
<br />
{factorials.join(", ")}
</p>
<button onClick={() => setCounter(counter + 1)}>Sayaç: {counter}</button>
</div>
);
}
Diyelim ki uygulamamıza canlı bir saat ekledik:
function useClock() {
const [time, setTime] = React.useState(new Date());
React.useEffect(() => {
const timer = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(timer);
}, []);
return time;
}
function Clock() {
const time = useClock();
return <div>{time.toLocaleTimeString()}</div>;
}
Eğer saat App bileşeninde yönetiliyorsa, her saniye time değiştiği için App yeniden render olur ve ağır faktöriyel hesaplamaları her saniye tekrarlanır.
useMemo burada da hayat kurtarır. Hesaplama sadece sayı değiştiğinde yapılır.
Yukarıdaki durumu daha temiz hale getirmek için:
function FactorialDisplay({ num }) {
const factorials = React.useMemo(() => {
const results = [];
for (let i = 1; i <= num; i++) {
results.push(factorial(i));
}
return results;
}, [num]);
return <p>{factorials.join(", ")}</p>;
}
function App() {
const [num, setNum] = React.useState(10);
const [counter, setCounter] = React.useState(0);
return (
<>
<Clock />
<label>
Bir sayı girin:
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
/>
</label>
<FactorialDisplay num={num} />
<button onClick={() => setCounter(counter + 1)}>Sayaç: {counter}</button>
</>
);
}
Eğer FactorialDisplay bileşenini React.memo ile sararsak, prop değişmediği sürece render olmaz.
const MemoizedFactorialDisplay = React.memo(FactorialDisplay);
_ Önemli bir nokta: React, render işlemlerini oldukça verimli ve optimize edilmiş şekilde yönetir. Bu yüzden çoğu durumda useMemo kullanmaya gerek kalmaz. useMemo kullanımı da kendi içinde bir maliyet taşır; hesaplamayı ve bağımlılıkları takip etmek, önbelleği yönetmek ekstra işlem demektir. Bu nedenle, gereksiz veya aşırı useMemo kullanımı, tam tersine performansı olumsuz etkileyebilir. Bu yüzden useMemo'yu yalnızca gerçekten ağır ve maliyetli hesaplamaları optimize etmek gerektiğinde kullanmak en iyisidir. _
Diyelim ki elimizde, kullanıcıların favori meyvelerini listeleyen küçük bir bileşen var: FavoriteFruits.
import React from "react";
const FavoriteFruits = React.memo(function FavoriteFruits({ fruits }) {
console.log("FavoriteFruits render oldu!");
return (
<ul>
{fruits.map((fruit) => (
<li key={fruit}>{fruit}</li>
))}
</ul>
);
});
export default FavoriteFruits;
Dışarıdan aldığı fruits isimli bir diziyle listeyi gösteriyor. React.memo sayesinde, fruits değişmediği sürece tekrar render olmaması gerekir.
import React, { useState } from "react";
import FavoriteFruits from "./FavoriteFruits";
function App() {
const [username, setUsername] = useState("");
// Bu liste sabit, değişmiyor
const fruits = ["Elma", "Muz", "Çilek"];
return (
<>
<input
placeholder="İsminizi yazın"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<h2>{username ? `Merhaba, ${username}!` : "Merhaba!"}</h2>
<FavoriteFruits fruits={fruits} />
</>
);
}
export default App;
Burada fruits dizisi sabit ve değişmediğini varsayıyoruz. O zaman kullanıcı sadece isim yazarken FavoriteFruits bileşeninin tekrar render olmaması gerekir, değil mi?
Konsola baktığımızda, her harf yazdığımızda FavoriteFruits render oldu! mesajı sürekli çıkıyor.
Neden? Çünkü fruits dizisini üst bileşen App her render ettiğinde yeniden oluşturuyoruz aslında.
JavaScript'te diziler ve nesneler referans ile karşılaştırılır, içerikle değil.
Örneğin:
const a = [1, 2, 3];
const b = [1, 2, 3];
console.log(a === b); // false
Görüyorsunuz, aynı elemanlara sahip olmalarına rağmen farklı diziler olarak kabul edilirler.
Biz fruits dizisini App fonksiyonunun içinde doğrudan tanımladık. React username değiştiğinde App tekrar render oluyor, dolayısıyla fruits dizisi yeniden oluşturuluyor.
Yani, görünürde aynı elemanlar var ama her render'da yeni bir dizi oluşturduğumuz için FavoriteFruitsa farklı bir prop gidiyor ve yeniden render oluyor.
Burada devreye yine React’in useMemo hook’u giriyor.
useMemo ile, bağımlılıklar değişmedikçe aynı dizi referansını kullanabiliriz:
import React, { useState, useMemo } from "react";
import FavoriteFruits from "./FavoriteFruits";
function App() {
const [username, setUsername] = useState("");
// useMemo ile fruits dizisini sadece bir kere oluşturuyoruz
const fruits = useMemo(() => ["Elma", "Muz", "Çilek"], []);
return (
<>
<input
placeholder="İsminizi yazın"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<h2>{username ? `Merhaba, ${username}!` : "Merhaba!"}</h2>
<FavoriteFruits fruits={fruits} />
</>
);
}
export default App;
useMemo'nun bağımlılık dizisi boş olduğu için, fruits dizisi sadece ilk render’da oluşturulup bellekte tutuluyor.
Bu sayede kullanıcı isim yazarken FavoriteFruits bileşeni tekrar render olmuyor.
Bence React performansını artırmak için useMemo ve React.memo() harika araçlar. Ama onları gereksiz yere kullanmamak da çok önemli. Çünkü bazen fazla optimizasyon karmaşaya sebep olabilir.
Kendi tecrübemden söyleyeyim, önce uygulamanızın yavaşlayıp yavaşlamadığını ölçün. Sonra gerçekten gerekliyse bu yöntemlere başvurun.
React’te useMemo'yu öğrendiysen ve “Tamam, referansları korumak önemliymiş” dediysen, şimdi sana onun kuzeni olan useCallback'i tanıtmak istiyorum.
Aslında işlevleri neredeyse aynı. Ama useCallback daha çok fonksiyon referanslarını korumak için kullanılır.
React bileşenleri her render edildiğinde, içinde tanımlı fonksiyonlar da yeniden oluşturulur.
Örneğin:
function App() {
const sayHello = () => {
console.log("Hello!");
};
}
Bu sayHello fonksiyonu her render’da yepyeni bir fonksiyondur. Aynı şeyi yapsa da, referansı farklıdır.
const one = () => 5;
const two = () => 5;
console.log(one === two); // false
Bu durum, özellikle React.memo ile sarmalanmış bileşenlere fonksiyon gönderirken sorun yaratır. Çünkü prop değişmiş gibi algılanır ve bileşen tekrar render olur.
Haydi bu bilgileri gerçek bir örnekle pekiştirelim.
import React from "react";
const SuperButton = React.memo(({ onSuperClick }) => {
console.log("SuperButton yeniden render edildi!");
return <button onClick={onSuperClick}>Süper Artır!</button>;
});
export default SuperButton;
Bu bileşen onSuperClick adında bir fonksiyon prop alıyor. Ve React.memo sayesinde sadece prop’lar değiştiğinde yeniden render oluyor.
import React, { useState } from "react";
import SuperButton from "./SuperButton";
function App() {
const [count, setCount] = useState(0);
const [user, setUser] = useState("");
const handleSuperClick = () => {
setCount((prev) => prev + 10);
};
return (
<div>
<input
placeholder="Adınız?"
value={user}
onChange={(e) => setUser(e.target.value)}
/>
<p>
Merhaba, {user || "ziyaretçi"}! Sayı: {count}
</p>
<SuperButton onSuperClick={handleSuperClick} />
</div>
);
}
export default App;
Bu senaryoda kullanıcı sadece adını yazıyor (user değişiyor), ama her harf yazıldığında SuperButton da yeniden render oluyor! Neden?
Çünkü handleSuperClick fonksiyonu her render’da yeniden tanımlanıyor → yeni referans → React.memo bunu “prop değişmiş” olarak algılıyor → yeniden render!
Artık useCallback zamanı:
import React, { useState, useCallback } from "react";
import SuperButton from "./SuperButton";
function App() {
const [count, setCount] = useState(0);
const [user, setUser] = useState("");
const handleSuperClick = useCallback(() => {
setCount((prev) => prev + 10);
}, []); // Bu fonksiyon sabit, hiç değişmez
return (
<div>
<input
placeholder="Adınız?"
value={user}
onChange={(e) => setUser(e.target.value)}
/>
<p>
Merhaba, {user || "ziyaretçi"}! Sayı: {count}
</p>
<SuperButton onSuperClick={handleSuperClick} />
</div>
);
}
İşte şimdi işler yolunda:
Çoook benziyorlar. Aslında şu iki satır aynı işlevi görür:
const fn1 = useMemo(() => () => {}, []);
const fn2 = useCallback(() => {}, []);
Yani useCallback, aslında useMemo'nun fonksiyonlar için özel bir kısayolu gibi düşünebilirsin. Aynı işi yapıyor, sadece niyetimizi daha açık ifade ediyor.
Her fonksiyona useCallback sarmaya gerek yok. Kullanman gereken bazı durumlar:
Kodu yazarken kendine şunu sor: “Bu fonksiyon her render’da yeniden oluşturulursa bir sorun olur mu?” Eğer cevap “evet” ise: useCallback zamanı gelmiş demektir.
Hey! Sonuna kadar okudun demek? İşte bu! 🚀
Bu yazının, React’in bazen karmaşık görünen ama bir o kadar da güçlü yönlerini anlamanda küçük de olsa bir katkı sağladığını umuyorum.
React'in ilk başta göz korkutucu olabileceğini biliyorum. Özellikle useMemo, useCallback gibi kavramlar, “Bu kadar şeyi gerçekten bilmem gerekiyor mu?” dedirtebilir. Ama işin güzelliği şu ki: Zamanla her şey yerine oturuyor.
Ben de bu süreci yaşayanlardan biriyim. Her satır kodda biraz daha öğrendim, her hata mesajında biraz daha cesaretlendim. Ve fark ettim ki, React sadece bir kütüphane değil — düşünce tarzını şekillendiren, sana daha temiz, daha sürdürülebilir kod yazmayı öğreten bir yapı.
Bu yazıda sadece teknik detaylara değil, aynı zamanda yazılımcı yolculuğunun içten yanlarına da dokunmaya çalıştım. Çünkü şunu biliyorum:
Eğer bu yazı sana bir şeyler kattıysa, ne mutlu bana. React ile olan yolculuğun boyunca sana bol bol keyifli öğrenmeler, sabırlı debug’lar ve mutlu console.loglar diliyorum. 😊
Yeni yazılarda görüşmek üzere! Kodla, dene, yanıl, öğren... ve en önemlisi: Tadını çıkar.
All rights reserved