Olay işleyicilerini React'te bağlamanın en iyi yolu

Resim: Flickr liz west tarafından

Bağlama olayı işleyicileri React'te zor olabilir (bunun için teşekkür etmek için JavaScript'iniz var). Perl ve Python'un tarihini bilenler için, TMTOWTDI (Bunu Yapmanın Birden Çok Yolu Var) ve TOOWTDI (Bunu Yapmanın Tek Yolu Var) tanıdık kelimeler olmalıdır. Ne yazık ki, en azından etkinlik bağlama için JavaScript, geliştiricilerin her zaman kafasını karıştıran bir TMTOWTDI dilidir.

Bu yazıda, React'te etkinlik bağlama oluşturmanın genel yollarını keşfedeceğiz ve size avantajlarını ve eksilerini göstereceğim. Ve en önemlisi, “Sadece Bir Yol” u bulmanıza yardım edeceğim - veya en azından favorim.

Bu gönderi, neden this.handler.bind (this) ya da function () arasındaki fark gibi yapmamız gereken bağlamanın gerekliliğini anladığınızı varsaymaktadır {console.log (this); } ve () => {console.log (this); }. Bu sorular hakkında kafanız karışırsa, Saurabh Misra'nın bunları açıklayan muhteşem bir yazısı vardı.

Render () içinde dinamik ciltleme

Yaygın olarak kullanılan ilk durum render () işlevinde .bind (this) 'i çağırmaktır. Örneğin:

sınıf HelloWorld, {bileşenini genişletiyor
  handleClick (olay) {}
  render () {
    dönüş (
      

Merhaba, {this.state.name}!

           );   } }

Tabii bu işe yarayacak. Fakat bir şeyi düşünün: Eğer bu.state.namechanges olursa ne olur?

This.state.name değerini değiştirmenin bileşenin yeniden oluşturmasına () neden olacağını söyleyebilirsiniz. İyi. Bileşen ad bölümünü güncellemek için işleyecektir. Ancak düğme oluşturulacak mı?

React'in Sanal DOM'yi kullandığı gerçeğini göz önünde bulundurun. Oluşturma işlemi gerçekleştiğinde, güncellenmiş Sanal DOM'yi önceki Sanal DOM ile karşılaştırır ve ardından yalnızca değiştirilen öğeleri gerçek DOM ağacına günceller.

Bizim durumumuzda render () çağrıldığında, this.handleClick.bind (this), işleyiciyi bağlamak için de çağrılır. Bu çağrı, render () ilk kez çağrıldığında kullanılan işleyiciden tamamen farklı yepyeni bir işleyici oluşturur!

Dinamik bağlama için sanal DOM. Mavi'deki öğeler yeniden işlenecek.

Yukarıdaki şemada olduğu gibi, render () daha önce çağrıldığında, this.handleClick.bind (this), funcA'yı döndürdü, böylece React onChange'in funcA olduğunu biliyordu.

Daha sonra, render () tekrar çağrıldığında, this.handleClick.bind (this), funcB döndürdü (her çağrıldığında yeni bir işlev döndürdüğünü unutmayın). Bu şekilde, React onChange'in artık funcA olmadığını biliyor, bu da düğmenin yeniden işlenmesi gerektiği anlamına geliyor.

Bir düğme bir sorun olmayabilir. Peki ya bir listede 100 düğme varsa?

render () {
  dönüş (
    {this.state.buttons.map (btn => (
      

Yukarıdaki örnekte, herhangi bir düğme etiketi değişikliği tüm düğmelerin yeniden oluşturulmasına neden olur çünkü tüm düğmeler yeni bir onChange işleyicisi oluşturur.

Yapıcıya bağlan ()

Eski bir okul yolu yapıcıya bağlayıcı yapmaktır. Hiçbir şey fantezi:

sınıf HelloWorld, {bileşenini genişletiyor
  constructor () {
    this.handleClick = this.handleClickFunc.bind (this);
  }
  render () {
    return (

Bu yol öncekinden çok daha iyi. Render () öğesini çağırmak, onClick için yeni bir işleyici oluşturmaz; bu nedenle, düğme değişmediği sürece yeniden oluşturulmaz.

Yapıcıda ciltleme için sanal DOM. Mavi'deki öğeler yeniden işlenecek.

Ok İşleviyle Bağlan

ES7 sınıfı özellikleriyle (şu anda Babel ile desteklenmektedir), yöntem tanımında ciltleme yapabiliriz:

sınıf HelloWorld, {bileşenini genişletiyor
  handleClick = (event) => {
    console.log (this.state.name);
  }
  render () {
    return ()
  }
}

Yukarıdaki kodda handleClick, eşdeğerde bir atamadır:

constructor () {
  this.handleClick = (event) => {...} ;
}

Yani bileşen başlatıldıktan sonra, this.handleClick bir daha asla değişmeyecek. Bu şekilde, 'nin yeniden oluşturulmamasını sağlar. Bu yaklaşım muhtemelen ciltleme yapmanın en iyi yoludur. Basit, okunması kolay ve en önemlisi çalışır.

Çoklu elemanlar için Ok Fonksiyonu ile dinamik ciltleme

Aynı ok fonksiyon numarasını kullanarak birden fazla giriş için aynı işleyiciyi kullanabiliriz:

sınıf HelloWorld, {bileşenini genişletiyor
  handleChange = name => event => {
    this.setState ({[name]: event.target.value});
  }
  render () {
    dönüş (
      
      
    )
  }
}

İlk bakışta, sadeliği nedeniyle bu oldukça şaşırtıcı görünüyor. Ancak, dikkatlice düşünürseniz, ilk yaklaşımla aynı sorunun olduğunu göreceksiniz: her seferinde render () her ikisi de olarak yeniden işlenir.

Aslında, bu yaklaşımın akıllı olduğunu düşünüyorum ve her alan için birden fazla handleXXXChange yazmak istemiyorum. Neyse ki, bu tür "çoklu kullanım işleyicisi" bir listede görünmesi daha az olasıdır. Bu, yeniden işlenen yalnızca birkaç bileşeni olacağı ve muhtemelen bir performans sorunu olmayacağı anlamına gelir.

Neyse, bize sağladığı faydalar performans kaybından çok daha büyük. Bu nedenle, bu yaklaşımı doğrudan kullanmanızı öneririm.

Bu performans sorunlarının belirginleşmesi durumunda, ciltleme yaparken işleyicileri önbelleğe almayı öneririm (ancak bu kodu daha az okunabilir hale getirir):

sınıf HelloWorld, {bileşenini genişletiyor
  handleChange = name => {
    if (! this.handlers [name]) {
      this.handlers [name] = event => {
        this.setState ({[name]: event.target.value});
      };
    }
    this.handlers [name];
  }
  render () {
    dönüş (
      
      
    )
  }
}

Sonuç

React'te olay ciltleri yaparken, işleyicilerin dinamik olarak üretilip üretilmediğini çok dikkatli kontrol etmeliyiz. Genellikle bu, etkilenen bileşenlerin yalnızca bir veya iki kez görünmesi durumunda bir sorun değildir. Ancak olay işleyicileri bir listede göründüğünde, bu ciddi performans sorunlarına neden olabilir.

Çözümler

  • Mümkün olduğunda Ok İşlev ciltlemeyi kullanın
  • Dinamik olarak ciltleme oluşturmanız gerekiyorsa, ciltler performans sorunu olursa, işleyicileri önbelleğe almayı düşünün

Okuduğunuz için teşekkürler! Umarım bu yazı yardımcı oldu. Bu yayını yararlı bulursanız, lütfen önererek daha fazla kişiyle paylaşın.

Güncelleme:

Omri Luzon ve Shesh, daha rahat ciltleme için lodash-dekoratörler ve reaksiyon-autobind paketlerinden bahsetti. Şahsen ben otomatik olarak bir şey yapmanın büyük bir hayranı değilim (her zaman bu tür ciltleri minimum tutmaya çalışıyorum) ama otomatik ciltleme kesinlikle temiz kod yazmak ve daha fazla çaba harcamak için harika bir yoldur. Kod şöyle olurdu:

autoBind'i 'tepki-autobind' öğesinden içe aktarın;
sınıf HelloWorld () {
  constructor () {
    AutoBind (bu);
  }
  handleClick () {
    ...
  }
  render () {
    return (

AutoBind, ciltlemeleri otomatik olarak işleyeceğinden, ciltleme işlemini yapmak için ok işlevini kullanmak gerekmez (handleClick = () => {}) ve render () işlevinde, this.handleClick doğrudan kullanılabilir.