React’te Refler İçin Bir Kılavuz
React, modern web uygulamaları geliştirmek için deklaratif bir yaklaşım sunar. Bileşenlerin durumu (state) ve özellikler (props) aracılığıyla veri akışını yöneterek kullanıcı arayüzünü tanımlarız. Ancak, bazen React’in deklaratif doğasının dışına çıkmamız ve doğrudan DOM elementleriyle veya bir bileşenin örneğiyle etkileşime geçmemiz gerekebilir. İşte tam bu noktada “Refler” (Refs) devreye girer. Refler, React bileşenleri içinde belirli bir DOM elementine veya bir sınıf bileşeninin örneğine doğrudan referans almamızı sağlayan bir “kaçış kapısı” (escape hatch) görevi görür.
Bu kılavuzda, React’te ref kavramını derinlemesine inceleyecek, farklı ref türlerini, kullanım senaryolarını, en iyi pratikleri ve kaçınılması gereken yaygın hataları ele alacağız. Amacımız, ref’leri ne zaman ve nasıl etkili bir şekilde kullanacağınızı anlamanıza yardımcı olmaktır.
Ref Nedir ve Neden İhtiyaç Duyarız?
React’in temel felsefesi, UI’ı uygulamanın durumunun bir fonksiyonu olarak görmektir. Yani, UI = f(state, props). Çoğu zaman, bir bileşenin görünümünü ve davranışını state ve props aracılığıyla yönetiriz. Örneğin, bir input alanının değerini veya bir düğmenin aktif/pasif durumunu state ile kontrol ederiz.
Ancak, bazı durumlar vardır ki React’in veri akışı modeli, ihtiyacımız olan kontrolü sağlamaz. Bu durumlar genellikle aşağıdaki kategorilere ayrılır:
- DOM Elementlerini Yönetme: Bir input alanına odaklanmak (focus), metin seçimi yapmak, medya oynatmayı başlatmak/durdurmak veya bir animasyonu tetiklemek gibi doğrudan DOM API’leri ile etkileşim gerektiren işlemler.
- Zorunlu Animasyonlar: React’in kendi animasyon mekanizmalarının yetersiz kaldığı veya üçüncü parti animasyon kütüphanelerinin doğrudan DOM manipülasyonu gerektirdiği durumlar.
- Üçüncü Parti DOM Kütüphaneleriyle Entegrasyon: D3.js, Chart.js gibi doğrudan DOM’u manipüle eden kütüphaneleri React uygulamalarına entegre ederken, bu kütüphanelerin çalışacağı belirli bir DOM elementine referans sağlamamız gerekebilir.
- Bir Child Component’in Metodlarına Erişme: Nadiren de olsa, bir üst bileşenden alt bileşenin belirli bir metodunu çağırmamız veya alt bileşenin iç state’ine/davranışına erişmemiz gerekebilir (örneğin, bir video oynatıcının
play()metodunu çağırmak).
Refler, bu tür senaryolarda React’in deklaratif modelinden geçici olarak ayrılmamızı ve zorunlu (imperative) kod yazmamızı sağlayan bir köprü görevi görür.
Ref Oluşturma ve Erişme
React’te ref oluşturmanın ve bunlara erişmenin birkaç yolu vardır. Temelde, bir ref oluşturduktan sonra, onu bir React elementine (
) atarız. React, bu ref’i ilgili DOM elementi veya bileşen örneğiyle doldurur.
React.createRef() (Sınıf Bileşenleri için)
Sınıf bileşenlerinde ref oluşturmanın standart yolu React.createRef() metodunu kullanmaktır. Bu metot, bileşen oluşturulduğunda bir ref nesnesi döndürür.
import React, { Component } from 'react';
class MyClassComponent extends Component {
constructor(props) {
super(props);
this.myInputRef = React.createRef(); // Ref oluşturma
}
componentDidMount() {
// Bileşen DOM'a bağlandığında input'a odaklan
this.myInputRef.current.focus();
}
render() {
return (
{/ Ref'i elemente atama /}
);
}
}
export default MyClassComponent;
Yukarıdaki örnekte:
this.myInputRef = React.createRef();ile bir ref nesnesi oluşturduk.ile bu ref’i input elementine atadık.componentDidMountiçindethis.myInputRef.current.focus();ile ref’e atanan DOM elementine erişerekfocus()metodunu çağırdık.
Ref nesnesinin current özelliği, ref’in atandığı DOM elementini veya bileşen örneğini tutar. React, DOM’a bir element bağlandığında current özelliğini doldurur ve element ayrıldığında null yapar.
useRef() Hook (Fonksiyonel Bileşenler için)
Fonksiyonel bileşenlerde ref kullanmak için useRef hook’unu kullanırız. useRef hook’u, React.createRef()‘e benzer bir ref nesnesi döndürür, ancak fonksiyonel bileşenlerin yaşam döngüsüne uygun olarak çalışır.
import React, { useRef, useEffect } from 'react';
function MyFunctionalComponent() {
const myInputRef = useRef(null); // Ref oluşturma, başlangıç değeri null
useEffect(() => {
// Bileşen DOM'a bağlandığında input'a odaklan
if (myInputRef.current) {
myInputRef.current.focus();
}
}, []); // Sadece bir kere çalışmasını sağla (componentDidMount benzeri)
const handleClick = () => {
if (myInputRef.current) {
alert('Input değeri: ' + myInputRef.current.value);
}
};
return (
{/ Ref'i elemente atama /}
);
}
export default MyFunctionalComponent;
useRef‘in önemli bir özelliği, sadece DOM referansları için değil, aynı zamanda render’lar arasında değişmeden kalması gereken herhangi bir mutable değeri saklamak için de kullanılabilmesidir. useRef tarafından döndürülen ref nesnesi, .current özelliği her zaman aynı nesneye işaret ettiği için, bir state gibi re-render tetiklemeden değerini değiştirebilirsiniz.
import React, { useRef, useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const renderCount = useRef(0); // Render sayısını tutmak için ref
// Her render'da renderCount.current değerini artır
renderCount.current = renderCount.current + 1;
return (
Count: {count}
This component has rendered {renderCount.current} times.
);
}
export default Counter;
Bu örnekte, renderCount bir state olmadığı için değeri değiştiğinde bileşen yeniden render edilmez. Ancak, count state’i değiştiğinde bileşen yeniden render edilir ve renderCount.current değeri güncel haliyle ekranda gösterilir. Bu, useRef‘in state’ten farklı olarak re-render tetiklememesi özelliğini gösterir.
Ref Kullanım Senaryoları Detaylı İnceleme
1. DOM Elementlerini Yönetme
En yaygın ref kullanım senaryosudur. Bir input’a otomatik odaklanma, kaydırma pozisyonunu kontrol etme veya bir elementin boyutunu ölçme gibi işlemler için ref’ler vazgeçilmezdir.
import React, { useRef } from 'react';
function ScrollToTopButton() {
const scrollableDivRef = useRef(null);
const scrollToTop = () => {
if (scrollableDivRef.current) {
scrollableDivRef.current.scrollTop = 0; // En üste kaydır
}
};
return (
Çok uzun bir içerik...
...
...
...
...
...
Daha fazla içerik...
);
}
export default ScrollToTopButton;
2. Üçüncü Parti Kütüphanelerle Entegrasyon
React, sanal DOM ile çalışırken, bazı kütüphaneler doğrudan gerçek DOM’u manipüle eder. Bu kütüphaneleri React ile kullanırken, kütüphanenin çalışacağı bir DOM elementine ref aracılığıyla erişim sağlamamız gerekir.
import React, { useRef, useEffect } from 'react';
import Chart from 'chart.js/auto'; // Örnek bir üçüncü parti kütüphane
function ChartComponent() {
const chartRef = useRef(null);
const chartInstance = useRef(null); // Chart örneğini saklamak için
useEffect(() => {
if (chartRef.current) {
// Önceki chart örneğini yok et (eğer varsa)
if (chartInstance.current) {
chartInstance.current.destroy();
}
// Yeni bir Chart.js grafiği oluştur
chartInstance.current = new Chart(chartRef.current, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
}
// Bileşen unmount edildiğinde chart'ı yok et
return () => {
if (chartInstance.current) {
chartInstance.current.destroy();
}
};
}, []); // Sadece bir kere çalış
return (
);
}
export default ChartComponent;
3. Bir Child Component’in Metodlarına Erişme (forwardRef ve useImperativeHandle)
Normalde, bir üst bileşen alt bileşenin iç detaylarına erişemez. Ancak bazı özel durumlarda, bir alt bileşenin belirli metodlarını veya özelliklerini dışarıya açmak isteyebiliriz. Bu, özellikle alt bileşen bir DOM elementi yerine kendi içinde mantık barındıran bir React bileşeni olduğunda geçerlidir.
forwardRef (Refleri İletme)
React bileşenleri varsayılan olarak ref prop’unu alt bileşenlerine iletmez. Bir bileşene ref atadığınızda, bu ref doğrudan bileşenin kök DOM elementine atanır (eğer bileşen bir DOM elementi döndürüyorsa) veya bir hata verir (eğer bileşen bir fonksiyonel bileşen ise ve forwardRef kullanılmamışsa). forwardRef, bir üst bileşenden gelen ref’i bir alt bileşenin içindeki bir DOM elementine veya başka bir React bileşenine iletmemizi sağlar.
import React, { forwardRef, useRef } from 'react';
// Alt bileşen: MyInput
// forwardRef kullanarak ref'i props olarak alabiliriz
const MyInput = forwardRef((props, ref) => {
return ;
});
// Üst bileşen: ParentComponent
function ParentComponent() {
const inputRef = useRef(null);
const focusInput = () => {
if (inputRef.current) {
inputRef.current.focus(); // MyInput içindeki gerçek input elementine odaklan
}
};
return (
);
}
export default ParentComponent;
Burada MyInput bileşeni, forwardRef sayesinde ParentComponent‘ten gelen inputRef‘i kendi içindeki elementine iletebiliyor.
useImperativeHandle (Fonksiyonel Bileşenlerde İç Metodları Açığa Çıkarma)
forwardRef ile birlikte kullanılan useImperativeHandle hook’u, bir fonksiyonel bileşenden dışarıya, ref aracılığıyla erişilebilecek belirli metodları veya değerleri özelleştirmemizi sağlar. Bu, alt bileşenin iç detaylarını gizlerken, dışarıya sadece ihtiyaç duyulan arayüzü sunmak için harika bir yoldur.
import React, { forwardRef, useRef, useImperativeHandle, useState } from 'react';
// Alt bileşen: CustomInput
const CustomInput = forwardRef((props, ref) => {
const [value, setValue] = useState('');
const inputEl = useRef(null);
// useImperativeHandle ile dışarıya açılacak metodları tanımlıyoruz
useImperativeHandle(ref, () => ({
focus: () => {
inputEl.current.focus(); // İçteki input elementine odaklan
},
clear: () => {
setValue(''); // Input değerini temizle
},
getValue: () => {
return value; // Mevcut değeri döndür
}
}));
return (
setValue(e.target.value)}
{...props}
/>
);
});
// Üst bileşen: App
function App() {
const customInputRef = useRef(null);
const handleFocus = () => {
if (customInputRef.current) {
customInputRef.current.focus(); // CustomInput'ın focus metodunu çağır
}
};
const handleClear = () => {
if (customInputRef.current) {
customInputRef.current.clear(); // CustomInput'ın clear metodunu çağır
}
};
const handleGetValue = () => {
if (customInputRef.current) {
alert('Input değeri: ' + customInputRef.current.getValue());
}
};
return (
);
}
export default App;
Bu örnekte, CustomInput bileşeni içindeki inputEl ref’i doğrudan dışarıya açmak yerine, useImperativeHandle kullanarak focus, clear ve getValue gibi spesifik metodları customInputRef.current üzerinden erişilebilir hale getirdik. Bu, alt bileşenin implementasyon detaylarını gizlerken, üst bileşene kontrollü bir arayüz sunar.
Callback Refleri
Daha eski bir yöntem olmasına rağmen, callback ref’leri hala geçerli ve bazı durumlarda daha esnek olabilir. Callback ref’leri, ref’in ayarlandığı ve sıfırlandığı anlar üzerinde daha fazla kontrol sağlar.
import React, { useState, useEffect } from 'react';
function CallbackRefComponent() {
const [height, setHeight] = useState(0);
let myDivElement = null; // DOM elementini saklamak için değişken
const setMyDivRef = (element) => {
myDivElement = element; // Callback çağrıldığında element buraya atanır
if (myDivElement) {
console.log('Div elementi ayarlandı:', myDivElement);
}
};
useEffect(() => {
// Bileşen mount edildiğinde veya güncellendiğinde yüksekliği ölç
if (myDivElement) {
setHeight(myDivElement.clientHeight);
}
}, [myDivElement]); // myDivElement değiştiğinde tetiklenir
return (
Bu div'in yüksekliği:
{height}px
Daha fazla içerik ekleyelim...
);
}
export default CallbackRefComponent;
Callback ref’leri, ref’in atandığı veya sıfırlandığı zamanlarda belirli eylemleri gerçekleştirmek istediğinizde kullanışlıdır. Özellikle, ref’in bir elementten ayrıldığı (unmount) anı yakalamak için useEffect‘in temizleme fonksiyonu ile birlikte kullanılabilirler.
Ref Kullanımında Dikkat Edilmesi Gerekenler ve En İyi Pratikler
Refler güçlü araçlar olsa da, React’in deklaratif doğasına aykırı oldukları için dikkatli kullanılmalıdır. Genellikle, bir görevi state ve props kullanarak çözemediğinizde bir “kaçış kapısı” olarak düşünülmelidirler.
1. Refs’i “Kaçış Kapısı” Olarak Görün
React’in temel prensibi deklaratif programlamadır. Bir bileşenin ne yapması gerektiğini değil, nasıl görünmesi gerektiğini tanımlarsınız. State ve props, bu deklaratif modelin temelidir. Refler, bu modelden çıkıp doğrudan DOM’u veya bir bileşen örneğini zorunlu olarak manipüle etmeniz gerektiğinde kullanılır. Bu nedenle, bir problemi çözmek için önce state ve props kullanmayı düşünün. Eğer bu yeterli değilse, ref’lere başvurun.
2. Refleri Abartmayın
Gereksiz yere ref kullanmak, kodunuzu daha karmaşık, bakımı zor ve hatalara açık hale getirebilir. Her zaman ref kullanmadan bir çözüm olup olmadığını sorgulayın. Örneğin, bir input’un değerini almak için ref yerine onChange event’i ile state kullanmak genellikle daha iyi bir yaklaşımdır.
3. Reflerin Bileşen Yaşam Döngüsü ile İlişkisi
- Reflerin
currentözelliği, bileşen DOM’a bağlandıktan sonra doldurulur. Bu nedenle, sınıf bileşenlerindecomponentDidMountiçinde, fonksiyonel bileşenlerde iseuseEffectiçinde ref’lere erişmek en güvenli yoldur. - Bir bileşen unmount edildiğinde, ref’in
currentözelliğinullolur.useEffect‘in temizleme fonksiyonu, bu durumu yakalamak ve gerekli temizliği yapmak için kullanılabilir (örneğin, üçüncü parti bir kütüphane tarafından oluşturulan bir örneği yok etmek).
4. Refleri Prop Olarak Geçirmeyin
Refler, özel bir React prop’u olarak ele alınır ve normal prop’lar gibi alt bileşenlere otomatik olarak iletilmez. Eğer bir ref’i bir alt bileşenin DOM elementine veya örneğine iletmek istiyorsanız, React.forwardRef kullanmalısınız. Bir ref’i normal bir prop olarak (örneğin, myRefProp={someRef}) geçirmek, React’in ref mekanizmasıyla çakışmalara yol açabilir ve beklenmedik davranışlara neden olabilir.
5. Refleri Fonksiyonel Bileşenlere Atarken Dikkat
Bir fonksiyonel bileşene doğrudan ref atayamazsınız (). Çünkü fonksiyonel bileşenlerin bir örneği yoktur. Eğer bir fonksiyonel bileşenin içindeki bir DOM elementine veya useImperativeHandle ile dışarıya açtığı bir değere ref ile erişmek istiyorsanız, o fonksiyonel bileşeni forwardRef ile sarmalamanız gerekir.
6. Performans Etkisi
Reflerin kendileri genellikle performans üzerinde büyük bir etkiye sahip değildir. Ancak, ref aracılığıyla yapılan doğrudan DOM manipülasyonları, React’in sanal DOM mekanizmasını atladığı için dikkatli kullanılmalıdır. Gereksiz veya sık DOM manipülasyonları performansı olumsuz etkileyebilir. Özellikle animasyonlar gibi durumlarda, tarayıcının render pipeline’ını anlamak ve requestAnimationFrame gibi API’leri kullanmak önemlidir.
7. Güvenlik ve Erişilebilirlik
Doğrudan DOM manipülasyonu yaparken, erişilebilirlik (Accessibility – A11y) kurallarına uymaya özen gösterin. Örneğin, bir input’a odaklanırken, klavye navigasyonu veya ekran okuyucular için uygun etiketlerin ve rollerin olduğundan emin olun.
Yaygın Hatalar ve Çözümleri
ref.current‘a erişmeden önce ref’in ayarlanmamış olması: Ref’ler, bileşen render edildikten ve DOM’a bağlandıktan sonra doldurulur.componentDidMountveyauseEffectiçinde erişmeye çalışın.- Fonksiyonel bileşenlerde
React.createRef()kullanmaya çalışmak: Fonksiyonel bileşenler içinuseRef()kullanın.React.createRef()sınıf bileşenleri içindir. - Refleri doğrudan fonksiyonel bileşenlere atamak: Bir fonksiyonel bileşenin içindeki bir DOM elementine erişmek için
forwardRefile sarmalayın ve ref’i içteki elemente iletin. - Gereksiz yere ref kullanmak: Bir state veya prop ile çözülebilecek bir problemi ref ile çözmeye çalışmak, kodu daha karmaşık hale getirir. Her zaman önce deklaratif çözümleri düşünün.
- Refleri normal prop’lar gibi geçirmek: Ref’ler özel bir prop’tur. Alt bileşene ref iletmek için
forwardRefkullanın.
Sonuç
Refler, React’in deklaratif dünyasında, doğrudan DOM elementleriyle veya bileşen örnekleriyle etkileşime geçmemiz gerektiğinde başvurduğumuz güçlü bir “kaçış kapısıdır”. Otomatik odaklanma, medya oynatma kontrolü, üçüncü parti kütüphanelerle entegrasyon ve alt bileşenlerin belirli metodlarına erişim gibi senaryolarda vazgeçilmezdirler. React.createRef(), useRef(), forwardRef, useImperativeHandle ve callback ref’leri gibi farklı mekanizmalar, bu ihtiyaçları karşılamak için React tarafından sunulur.
Ancak, ref’lerin dikkatli ve ölçülü kullanılması esastır. React’in temel felsefesine aykırı oldukları için, öncelikle state ve props tabanlı çözümler aranmalı, ref’ler yalnızca gerçekten gerekli olduğunda kullanılmalıdır. Doğru kullanıldığında, ref’ler React uygulamalarınıza esneklik ve güç katarken, yanlış veya gereksiz kullanımları kod karmaşıklığına ve bakım zorluklarına yol açabilir. Bu kılavuz, React’te ref’leri ne zaman ve nasıl etkili bir şekilde kullanacağınıza dair sağlam bir temel oluşturmanıza yardımcı olmayı umuyor.