Ref'ler ve DOM

Ref’ler, render metodu içerisinde oluşturulan DOM düğümümlerine veya React elemanlarına erişmeyi sağlar.

React’ın tipik veri akışında, prop’lar üst bileşenlerin alt bileşenleri ile etkileşime geçmelerinin tek yoludur. Bir alt bileşeni düzenlemek için, onu yeni prop’lar ile yeniden render edersiniz. Fakat, birkaç senaryo vardır ki bir alt bileşeni tipik veri akışının dışında mecburi olarak düzenlemeniz gerekebilir. Düzenlenecek olan bir alt bileşen, bir React bileşeni’nin nesnesi veya bir DOM elemanı olabilir. Her iki durum için de React bir çıkış yolu sağlar.

Ref’ler Ne Zaman Kullanılmalıdır

Ref’leri kullanmak için birkaç iyi kullanım senaryosu bulunmaktadır:

  • Focus olayını, metin seçmeyi veya yeniden ortam oynatmayı yönetmek,
  • Animasyonları tetiklemek,
  • Üçüncü-parti DOM kütüphanelerini entegre etmek

Bildirimsel (declarative) olarak halledilebilecek durumlar için ref’leri kullanmaktan kaçının.

Örneğin, bir Dialog bileşeni için open() ve close() metodlarını kullanmak yerine, isOpen prop’unu Dialog‘a atayabilirsiniz.

Ref’leri Aşırı Kullanmayın

Ref’leri kullanmaktaki ilk eğiliminiz uygulamanızdaki bazı şeyleri gerçekleştirmek için olabilir. Eğer durum bu ise bekleyin ve state’in bileşen hiyerarşisinde nerede tutulması gerektiği hakkında biraz daha eleştirel düşünün. Bununla ilgili örnekler için State’i Yukarı Taşıma rehberini inceleyebilirsiniz.

Not

Aşağıdaki örnekler React 16.3 ile gelen React.createRef() API’sini kullanabilmek için güncellenmiştir. React’in önceki sürümlerini kullanıyorsanız, callback ref’lerini kullanmanızı tavsiye ederiz.

Ref’ler Oluşturma

Ref’ler, React.createRef() kullanılarak oluşturulur ve React elemanlarına ref özelliğini kullanarak eklenir. Ref’ler genellikle bir bileşen oluşturulduğunda, bir nesnenin özelliğine atanır. Böylelikle refler bileşen boyunca referans alınabilir.

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

Ref’lere Erişim

Bir ref, render içerisinde bir elemena aktarıldığında, o düğüme bağlı bir referans, ref’in current özelliğinde erişilebilir hale gelir.

const node = this.myRef.current;

Ref’in değeri, düğüm türüne bağlı olarak değişir.

  • ref özelliği bir HTML elemanında kullanıldığında, constructorda React.createRef() ile oluşturulan ref, esas DOM elemanını kendisinin current özelliği olarak alır.
  • ref özelliği özel bir sınıf bileşininde kullanıldığında, ref nesnesi yerleştirilmiş bileşeninin nesnesini current olarak alır.
  • ref özelliğini fonksiyon bileşeni içerisinde kullanamazsınız çünkü fonksiyon bileşenlerinin nesneleri olmaz.

Aşağıdaki örnekler farklılıkları göstermektedir.

DOM Elemanına Ref Ekleme

Bu kod, bir DOM düğümüne bağlı referans tutmak için ref kullanır:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // textInput DOM elemanını kaydetmek için bir ref oluşturun
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // DOM API kullanarak text input'a odaklanın
    // Not : DOM düğümünü getirmek için "current"ı kullanırız.
    this.textInput.current.focus();
  }

  render() {
    // React’a, constructor içerisinde oluşturduğumuz `textInput`u ile
    //<input> ref'i ile ilişkilendirmek istediğimizi belirtin.
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

Bileşen eklendiğinde, React, current özelliğini DOM elemanı ile atar ve bileşen çıkarıldığında geri null atanır. ref güncellemeleri componentDidMount veya componentDidUpdate yaşam döngüsü metodlarından önce gerçekleşir.

Sınıf Bileşenine Ref Ekleme

Yukarıdaki CustomTextInputun, eklendikten hemen sonra tıklandığı senaryosunu simüle etmek istediğimizde, özel input’a erişmek için ve onun focusTextInput metodunu çağırmak için ref kullanabiliriz.

class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focusTextInput();
  }

  render() {
    return (
      <CustomTextInput ref={this.textInput} />
    );
  }
}

Bu sadece CustomTextInput bir sınıf olarak tanımlandığında çalışır.

class CustomTextInput extends React.Component {
  // ...
}

Refler ve Fonksiyon Bileşenleri

ref özelliğini fonksiyon bileşeni içerisinde kullanmazsınız çünkü fonksiyon bileşenlerinin nesneleri olmaz.

function MyFunctionComponent() {
  return <input />;
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  render() {
    // Bu çalışmayacaktır!
    return (
      <MyFunctionComponent ref={this.textInput} />
    );
  }
}

Eğer bir ref’e ihtiyacınız varsa, bileşeni sınıfa dönüştürmelisiniz. Tıpkı yaşam döngüsü metodlarında veya state ihtiyacınız olduğunda yaptığınız gibi.

Bir DOM elemanına veya sınıf bileşenine işaret ettiğiniz sürece fonksiyon bileşeni içerisinde ref kullanabilirsiniz

function CustomTextInput(props) {
  //textInput'u burada tanımlanmalıdır. Böylelikle ref onu işaret edebilir
  let textInput = React.createRef();

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

DOM Ref’lerini Üst Bileşenlerde Açığa Çıkarma

Nadir durumlarda, bir alt bileşenin DOM düğümüne üst bileşenden erişmek isteyebilirsiniz. Bu genelde önerilmez; çünkü bileşenin kapsüllemesini (encapsulation) bozar. Ancak bazen odağı tetiklemek veya bir child DOM düğümünün boyutunu veya konumunu hesaplamak için faydalı olabilir.

Alt bileşene ref ekleyebilirsiniz. Ancak bu ideal bir çözüm değildir. DOM düğümünden ziyade sadece bir tane bileşen nesnesi alırsınız. Ek olarak bu, fonksiyon bileşenleri ile çalışmaz.

React 16.3 veya daha üst bir versiyonunu kullanırsanız, bu durumlar için Ref Yönlendirme kullanmanızı tavsite ederiz. Ref yönlendirme, bileşenlerin, alt bileşenin ref’ini kendilerinin gibi göstermesini sağlar. Bir alt bileşenin Dom düğümünü üst bileşende nasıl kullanacağınızın daha detaylı örneğini Ref Yönlendirme dökümanında bulabilirsiniz.

React 16.2 veya daha eski bir versiyonu kullanıyorsanız, veya ref yönlendirme ile sağlanan esneklikten daha fazla esnekliğe ihtiyacınız varsa, bu alternatif yaklaşımı kullanabilirsiniz ve bir ref’i farklı isimlendirilmiş bir prop olarak aktarabilirsiniz.

Mümkünse, DOM birimlerini açığa çıkarmamanızı tavsiye ederiz. Ancak faydalı bir kaçış yolu olabilir. Bu yaklaşımın, alt bileşene bazı kodlar eklemenizi gerektirdiğini unutmayın. Alt bileşen üzerinde herhangi bir kontrolünüz yoksa, son seçeneğiniz findDOMNode() kullanmak olabilir. Ama bu StrictMode içerisinde kullanımdan kaldırılmıştır.

Callback Refs

React ayrıca, “callback refs” adı verilen refleri ayarlamanın başka bir yolunu da destekler. Bu, ref’ler ayarlandıklarında veya ayarlanmadıkları zamanlarda daha fazla kontrol’e sahip olmalarını sağlar.

createRef() tarafından oluşturulan bir refi aktarmaktansa, bir fonksiyon aktarabilirsiniz. Fonksiyon, React bileşeninin nesnesini veya HTML DOM elemanını bir argüman olarak alır, böylelikle bileşenin nesnesi başka bir yerde saklanabilir ve erişilebilir.

Aşağıdaki örnekte yaygın bir kullanım uygulanmıştır. ref callback’i kullanarak bir nesnenin özelliğinde DOM düğümüne bir referans kaydedilir.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // Focus the text input using the raw DOM API
    // DOM API kullanark text input'a odaklanın
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    // Eklendikten sonra otomatik olarak odaklanma
    this.focusTextInput();
  }

  render() {
    // Nesne alanında bulunan metin girdisi elemanına bir referans
    // tutmak için `ref` callback'i kullanın. (örneğin, this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

React, bileşen eklendiğinde DOM elemanı ile beraber ref callback’ini çağırır ve bileşen çıkarıldığında da null ile çağırır. Ref’lerin, componentDidMount veya componentDidUpdate tetiklenmeden önce güncel oldukları garanti edilir.

React.createRef() ile oluşturulan nesne ref’leri gibi, Callback ref’lerini de bileşenler arasında aktarabilirsiniz.

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}

Yukarıdaki örnekte, Parent, ref callback’ini inputRef prop’u olarak CustomTextInputuna aktarır ve CustomTextInputu aynı fonksiyonu özel bir ref özelliği olarak <input>a aktarır. Sonuç olarak, Parenttaki this.inputElementi, CustomTextInputtaki <input> elemanına karşılık gelen DOM düğümüne set edilir.

Eski API: String Refler

Daha önceden React ile uğraştıysanız, ref özelliğinin "textInput" gibi bir string olduğu ve DOM düğümüne de this.refs.textInput şeklinde erişildiği eski API’a aşina olabilirsiniz. String ref’lerin bazı sorunlarının olması ve muhtemelen gelecek sürümlerden birinde kaldırılacağı için, bu kullanımı önermiyoruz.

Not

Eğer this.refs.textInput‘u reflere erişmek için kullanıyorsanız, createRef API veya callback refleri seçeneklerinden birini kullanmanızı tavsiye ederiz.

Callback Ref’lerine Dair Uyarılar

Eğer ref callback bir satıriçi fonksiyon olarak tanımlanmışsa, güncelleme anında iki kez çağırılacaktır. İlk olarak null ile, sonrasında DOM elemanı yeniden çağrılır. Bunun sebebi, fonksiyonun bir nesnesi her render’da oluşturulur. Bu yüzden React eski ref’i kaldırır ve yenisini ekler. Bunu önlemek için ref callback’ini sınıfa bağlı bir metod olarak tanımlayabilirsiniz. Ancak bu, birçok durumda önemli değildir.