Foruma hoş geldin 👋, Ziyaretçi

Forum içeriğine ve tüm hizmetlerimize erişim sağlamak için foruma kayıt olmalı ya da giriş yapmalısınız. Foruma üye olmak tamamen ücretsizdir.

React'te bir event handler içinde state'in güncel değerini alamadığım closure sorunu ve useRef ile çözümüm

codrix

Üye
Katılım
14 Mart 2026
Mesajlar
45
Merhaba arkadaşlar, bugün başımı çok ağrıtan bir React sorununu ve nasıl temiz bir şekilde çözdüğümü anlatacağım. Özellikle setTimeout, setInterval veya bir event listener içinde state değerini okumaya çalıştığınızda, o anki değeri değil, sanki eski bir değeri okuyormuş gibi hissediyorsanız, bu yazı tam size göre.

🔥 Karşılaştığım Sorun

Bir sayaç uygulaması yapıyordum. Kullanıcı bir butona basınca sayaç başlayacak, 5 saniye sonra da o ana kadaki sayaç değerini console'a yazdıracaktı. Basit gibi görünüyordu değil mi? İlk kodum şöyleydi:

JavaScript:
function Counter() {
  const [count, setCount] = useState(0);

  const startTimer = () => {
    setCount(0); // Sıfırla
    const intervalId = setInterval(() => {
      setCount(prevCount => prevCount + 1); // Her saniye 1 artır
    }, 1000);

    // 5 saniye sonra durdur ve değeri yazdır
    setTimeout(() => {
      clearInterval(intervalId);
      console.log("Son count değeri:", count); // BURADA SORUN VAR!
    }, 5000);
  };

  return (
    <div>
      <p>Sayaç: {count}</p>
      <button onClick={startTimer}>Başlat</button>
    </div>
  );
}

Butona bastım, sayacın 0'dan 4'e kadar (5 saniyede) tıkır tıkır arttığını gördüm. Ama setTimeout'un içindeki console.log, her seferinde 0 yazdırdı! Kafayı yemiştim. Sayaç ekranda 4 gösterirken, neden fonksiyonun içinde gördüğüm değer 0'dı? İşte meşhur stale closure (bayat kapanış) tuzağına düşmüştüm. startTimer fonksiyonu çalıştığı anda, count değeri 0'dı ve setTimeout'un callback'i bu 0 değerini "hapsedip" saklamıştı. React state güncellendi ama bu callback, hapsolduğu eski değeri kullanmaya devam etti.

🔍 Sorunun Kökeni ve İlk Denemelerim

Sorunu anladıktan sonra ilk aklıma gelen, count değerini bir değişkende saklamak oldu. Ama bu da işe yaramadı çünkü değişken de aynı closure'ın içinde tanımlanıyordu.

Sonra "useEffect ile count değiştiğinde bir şey yapsam?" diye düşündüm. Fakat bu sefer de log'u her count değişiminde değil, sadece 5 saniye sonra atmak istiyordum. Kafam karışmıştı.

💡 useRef ile Gürültüsüz Çözüm

Aradan biraz zaman ve kahve geçtikten sonra aklıma useRef hook'u geldi. useRef, .current property'si ile değiştirilebilen bir değer tutar ve bu değer her render arasında kalıcıdır. En güzeli, değeri değiştiğinde bile bileşeni yeniden render ettirmez! Yani, güncel state değerini bir ref'in içinde de saklayabilirim. İşte benim kullandığım en temiz çözüm:

JavaScript:
function CounterSolution() {
  const [count, setCount] = useState(0);
  const countRef = useRef(count); // Ref oluştur, başlangıç değeri state ile aynı

  // count her değiştiğinde, ref'in current değerini güncelle!
  useEffect(() => {
    countRef.current = count;
  }, [count]);

  const startTimer = () => {
    setCount(0);
    const intervalId = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);

    setTimeout(() => {
      clearInterval(intervalId);
      // Artık güncel değeri ref'ten okuyabilirim!
      console.log("Son count değeri (Ref'ten):", countRef.current);
    }, 5000);
  };

  return (
    <div>
      <p>Sayaç: {count}</p>
      <button onClick={startTimer}>Başlat</button>
    </div>
  );
}

Bu pattern ile olay şu: State (count) değiştiğinde, useEffect tetikleniyor ve countRef.current değeri en güncel count değeri ile güncelleniyor. setTimeout'un callback'i çalıştığında ise, o anki state değerini değil (ki o hala eski closure değeri), ref'in içindeki güncel .current değerini okuyor. Sorun çözüldü!

🎯 Özet ve Kullanım Alanları

Bu yöntem sadece setTimeout/setInterval için değil, aşağıdaki durumlarda da hayat kurtarıcı:
Event listener'lar (window.addEventListener)
API isteklerinin callback'leri
Herhangi bir asenkron işlemde, işlem başladığı andaki state'i değil de, tamamlandığı andaki state'i okumak istediğinizde.

Önemli nokta: Ref'i ekranda göstermek için kullanmıyoruz. Sadece değeri anlık okumak için bir "referans" olarak kullanıyoruz. Render işlemi hâlâ state'e (count) bağlı.

Peki ya siz? React'te benzer bir closure tuzağına düştünüz mü? setTimeout dışında hangi senaryolarda bu sorunla karşılaştınız? Belki sizin kullandığınız daha farklı bir yöntem vardır, yorumlarda paylaşın tartışalım!
 

Tema özelleştirme sistemi

Bu menüden forum temasının bazı alanlarını kendinize özel olarak düzenleye bilirsiniz.

Zevkine göre renk kombinasyonunu belirle

Tam ekran yada dar ekran

Temanızın gövde büyüklüğünü sevkiniz, ihtiyacınıza göre dar yada geniş olarak kulana bilirsiniz.

Geri