Exploring Infinite Possibilities on FrontEnd 🚀
Burak Sağlık
Search...

Fri Aug 08 2025

useMemo ve useCallback Kafanı Karıştırıyorsa…

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!


🎯 Temel Fikir: useMemo Ne İşe Yarar?

Başlamadan önce: useMemo tam olarak ne yapar?

_ 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 Nasıl Render Eder?

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.

Bu, şöyle düşünülebilir:

  • Uygulama durumu değiştiğinde, React o anki verilerle nasıl bir UI olması gerektiğini yeniden hesaplar.
  • Bu hesaplamalar sonucunda ortaya çıkan yapı, bir nevi anlık görüntü (snapshot) gibidir.
  • Bu “fotoğraf”, tarayıcıya çizdirilecek HTML'in ya da stilin JS nesneleriyle temsilidir.
  • React bu yapıya virtual DOM (sanal DOM) der.

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.

❗️Peki Sorun Nerede Başlıyor?

React, kendi başına oldukça optimize bir sistemdir. Ancak bazı durumlarda bu yeniden oluşturma süreci pahalıya mal olabilir.

Örneğin:

  • Her render'da tekrar hesaplanan ağır matematiksel işlemler,
  • Her seferinde yeniden oluşturulan büyük diziler veya objeler,
  • Ya da her defasında yeniden tanımlanan fonksiyonlar,

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.

🛠 useMemo ve useCallback Tam Olarak Burada Devreye Girer

React'teki useMemo ve useCallback hook’ları, bu tür gereksiz iş yüklerini azaltmamıza yardımcı olur. İki temel optimizasyon sağlarlar:

  • Ağır işlemlerin yeniden hesaplanmasını önlerler. (Örn: useMemo ile pahalı bir hesaplama yalnızca gerekli olduğunda yapılır.)
  • Bileşenlerin gereksiz yere yeniden render edilmesini engellerler. (Örn: useCallback ile aynı fonksiyon referansı korunur, bu da çocuk bileşenlerde yeniden render tetiklemez.)

Bu iki stratejiyi daha iyi anlamak için sıradaki bölümlerde örneklerle detaylıca inceleyeceğiz.


1: Ağır Hesaplamalar — useMemo ile Gereksiz Yeniden Hesaplamaları Önlemek

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.

İlk Uygulama: Faktöriyel Hesaplama

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?

  • Kullanıcı bir sayı giriyor.
  • num değiştikçe 1’den num'a kadar faktöriyel hesaplanıyor.
  • Ayrıca sayacı artıran bir buton var.
  • Ancak her counter tıklamasında React bileşeni tekrar render ediyor ve faktöriyel hesaplamaları tekrar çalışıyor.
  • counter ve num birbirinden akakasız olsada counter değiştiği duruda yeniden render işlemi yapılıyor

Neden bu sorun?

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:

  • Karmaşık ve zaman alan hesaplamalar,
  • Büyük veri setleri,
  • Sık sık render edilen bileşenlerde

performans sorunlarına yol açabilir.

useMemo ile Çözüm: Hesaplamayı Hatırlamak (Memoization)

React, böyle maliyetli hesaplamaları sadece gerektiğinde tekrar yapmamızı kolaylaştırır: useMemo adındaki hook ile.

useMemo nasıl çalışır?

  • İçine verilen fonksiyonu, belirtilen bağımlılıklar (dependency array) değiştiğinde çalıştırır.
  • Bağımlılıklar değişmediyse, daha önce hesaplanmış sonucu döndürür.
  • Bu şekilde gereksiz hesaplamaların önüne geçilir.

useMemo kullanarak faktöriyel hesaplamasını optimize etmek

const factorials = React.useMemo(() => {
  const results = [];
  for (let i = 1; i <= num; i++) {
    results.push(factorial(i));
  }
  return results;
}, [num]);

Yukarıdaki kodda

  • num değiştiğinde fonksiyon tekrar çalışır.
  • num aynı kaldığı sürece, önceki hesaplama sonucu kullanılır.

Güncellenmiş Uygulama

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>
  );
}

Bu sayede ne oluyor?

  • counter her arttığında render çağrılır,
  • Ancak faktöriyel hesaplama yapılmaz, çünkü num değişmemiştir,
  • Uygulama çok daha hızlı ve akıcı çalışır.

Daha gerçekçi bir senaryo: Dijital saat eklemek

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.

Daha İyi Mimari: Bileşenleri Ayrıştırmak

Yukarıdaki durumu daha temiz hale getirmek için:

  • Saat bileşenini ayrı tut,
  • Faktöriyel hesaplayan bileşeni ayrı tut.
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>
    </>
  );
}
  • Böylece saat değiştiğinde sadece Clock render olur,
  • Faktöriyel hesaplama sadece num değiştiğinde çalışır.

React.memo ile Daha Fazla Optimizasyon

Eğer FactorialDisplay bileşenini React.memo ile sararsak, prop değişmediği sürece render olmaz.

const MemoizedFactorialDisplay = React.memo(FactorialDisplay);

Özet

  • React her render’da fonksiyonları yeniden çalıştırır.
  • Ağır hesaplamalar için useMemo kullanmak gereksiz tekrarlamaları önler.
  • Bileşenleri mantıklı şekilde bölmek render maliyetini azaltır.
  • React.memo ile bileşen renderlarını kontrol etmek performansı artırır.

_ Ö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. _

2: Korunan Referanslar

Hadi Pratik Bir Örnek Üzerinden Anlayalım

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.

Şimdi de bu bileşeni kullanan üst bileşenimiz var:

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?

Ama İşte Problem!

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.

Bu Nasıl Oluyor?

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.

Üstteki Kodda fruits Nasıl Her Seferinde Yenileniyor?

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.

Peki Bunu Nasıl Çözeriz?

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;

Artık fruits referansı hep aynı

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.

Özetle

  • React, objeleri/dizileri prop olarak geçirirken, referans bazlı karşılaştırma yapar.
  • Eğer her render’da yeni bir dizi ya da nesne oluşturuyorsak, React bunu farklı kabul eder ve memo’lanmış bileşenler yeniden render edilir.
  • Bu tür durumlarda useMemo kullanarak referansı korumak performansı artırır.
  • Ancak unutmayalım, useMemo ağır hesaplamalar için değil, referans sabitliği için kullanılır.

Küçük Samimi Not

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.

useCallback Hook'u: Fonksiyon Referanslarını Korumanın Yolu

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.

Neden Gerek Var?

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.

Pratik Bir Örnek: SuperButton

Haydi bu bilgileri gerçek bir örnekle pekiştirelim.

  1. SuperButton bileşeni
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.

  1. Ana bileşenimiz: App
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!

Çözüm: useCallback ile Fonksiyonu Sabitlemek

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:

  • Kullanıcı yazı yazdığında sadece App bileşeni render olur.
  • SuperButton, handleSuperClick fonksiyonu değişmediği için yeniden render edilmez.

Peki useMemo ile Farkı Ne?

Ç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.

🧠 Kural basit:
  • "Bir değeri (dizi, nesne vs.) sabit tutmak istiyorsan → useMemo"
  • "Bir fonksiyonu sabit tutmak istiyorsan → useCallback"

useCallback Ne Zaman Kullanılır?

Her fonksiyona useCallback sarmaya gerek yok. Kullanman gereken bazı durumlar:

  • ✅ React.memo ile sarmalanmış bir bileşene fonksiyon gönderiyorsan
  • ✅ Fonksiyonu useEffect veya başka bir memoize kancanın içinde bağımlılık olarak kullanıyorsan
  • ✅ Yüksek sayıda render olan performans hassas bileşenlerde, özellikle büyük listelerde (örn. FlatList, VirtualizedList)

Ne Zaman Kullanma?

  • ⛔ Kodun sade kalması önemliyse
  • ⛔ Memoize edilen fonksiyonun kendisi basitse ve çok sık kullanılmıyorsa
  • ⛔ Erken optimizasyon yapıyorsan (önce performans sorunu yaşa, sonra optimize et)

Özetle

  • useCallback, aynı fonksiyonun referansını yeniden kullanmak için var.
  • React.memo ile birlikte muazzam performans artışları sağlayabilir.
  • Ama abartılı kullanmak kodu karmaşıklaştırabilir.
  • useCallback, useMemo(() => fn, deps) ile aynı ama daha okunabilir.
Bonus Tavsiye 👇

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.

Burak Sağlık

Burak Saglik

©2024 Desing and Developed by @Burak Sağlık

All rights reserved