JSX props neden ok işlevlerini kullanmamalı veya bağlamamalı?


105

React uygulamamla lint çalıştırıyorum ve şu hatayı alıyorum:

error    JSX props should not use arrow functions        react/jsx-no-bind

Ok işlevini çalıştırdığım yer burası (içeride onClick):

{this.state.photos.map(tile => (
  <span key={tile.img}>
    <Checkbox
      defaultChecked={tile.checked}
      onCheck={() => this.selectPicture(tile)}
      style={{position: 'absolute', zIndex: 99, padding: 5, backgroundColor: 'rgba(255, 255, 255, 0.72)'}}
    />
    <GridTile
      title={tile.title}
      subtitle={<span>by <b>{tile.author}</b></span>}
      actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color="white"/></IconButton>}
    >
      <img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: 'pointer'}}/>
    </GridTile>
  </span>
))}

Bu kaçınılması gereken kötü bir uygulama mı? Ve bunu yapmanın en iyi yolu nedir?

Yanıtlar:


170

JSX props'larında neden satır içi ok işlevlerini kullanmamalısınız?

Ok işlevlerini kullanmak veya JSX'te bağlamayı kullanmak, performansa zarar veren kötü bir uygulamadır çünkü işlev her işlemede yeniden oluşturulur.

  1. Bir işlev oluşturulduğunda, önceki işlev çöp olarak toplanır. Birçok öğeyi yeniden işlemek, animasyonlarda şaka yaratabilir.

  2. Bir satır içi ok işlevinin PureComponentkullanılması shallowCompare, shouldComponentUpdateyöntemde kullanılan e-postaların ve bileşenlerin yine de yeniden oluşturulmasına neden olur . Ok fonksiyonu pervanesi her seferinde yeniden oluşturulduğundan, yüzeysel karşılaştırma bunu bir pervane için bir değişiklik olarak tanımlayacak ve bileşen yeniden oluşturulacaktır.

Aşağıdaki 2 örnekte görebileceğiniz gibi - satır içi ok işlevini kullandığımızda, <Button> bileşen her seferinde yeniden oluşturulur (konsol 'oluşturma düğmesi' metnini gösterir).

Örnek 1 - PureComponent olmadan içi işleyicisi

Örnek 2 - PureComponent ile içi işleyicisi

thisOk işlevlerini satır içi olmadan bağlama yöntemleri

  1. Yöntemi yapıcıda el ile bağlama:

    class Button extends React.Component {
      constructor(props, context) {
        super(props, context);
    
        this.cb = this.cb.bind(this);
      }
    
      cb() {
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }
  2. Ok işlevi ile öneri sınıfı alanlarını kullanarak bir yöntemi bağlama . Bu bir 3. aşama önerisi olduğundan, babel yapılandırmanıza Aşama 3 ön ayarını veya Sınıf özellikleri dönüşümünü eklemeniz gerekir .

    class Button extends React.Component {
      cb = () => { // the class property is initialized with an arrow function that binds this to the class
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }

İç geri aramalara sahip İşlev Bileşenleri

Bir işlev bileşeninin içinde bir iç işlev (örneğin olay işleyicisi) oluşturduğumuzda, işlev bileşen her oluşturulduğunda yeniden oluşturulacaktır. İşlev, bir alt bileşene ( Buttonbu durumda) props olarak (veya bağlam aracılığıyla) aktarılırsa , bu çocuk da yeniden oluşturacaktır.

Örnek 1 - İç geri aramalı İşlev Bileşeni:

Bu sorunu çözmek için geri aramayı kancaya sarabilirizuseCallback() ve bağımlılıkları boş bir diziye ayarlayabiliriz.

Not:useState oluşturulan işlevi mevcut durumunu sağlayan bir güncelleme fonksiyonu, kabul eder. Bu şekilde, mevcut durumu için bir bağımlılık ayarlamamıza gerek kalmaz useCallback.

Örnek 2 - useCallback ile sarmalanmış bir iç geri aramaya sahip İşlev Bileşeni:


3
Bunu, vatansız bileşenlerde nasıl başarırsınız?
lux

4
Durumsuz (işlev) bileşenlere sahip değildir this, bu nedenle bağlanacak hiçbir şey yoktur. Genellikle yöntemler, bir sarmalayıcı akıllı bileşen tarafından sağlanır.
Ori Drori

39
@OriDrori: Geri aramada veri aktarmanız gerektiğinde bu nasıl çalışır? onClick={() => { onTodoClick(todo.id) }
adam-beck

4
@ adam-beck - sınıftaki geri çağrı yöntemi tanımının içine ekleyin cb() { onTodoClick(this.props.todo.id); }.
Ori Drori

2
@ adam-beck Bunun useCallbackdinamik değerle nasıl kullanılacağını düşünüyorum . stackoverflow.com/questions/55006061/…
Shota Tamura

9

Bunun nedeni, bir JSX özelliğinde kullanılıyorsa, bir ok işlevinin her işlemede işlevin yeni bir örneğini oluşturmasıdır. Bu, çöp toplayıcı üzerinde büyük bir yük oluşturabilir ve ayrıca, işlevler yeniden kullanılmak yerine atılacağı için tarayıcının herhangi bir "etkin yolu" optimize etmesini engelleyebilir.

Tüm açıklamayı ve daha fazla bilgiyi https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md adresinde görebilirsiniz.


Sadece bu da değil. Her seferinde yeni işlev örneklerinin oluşturulması, durumun değiştirildiği ve bir bileşenin durumu değiştirildiğinde yeniden oluşturulacağı anlamına gelir. React'i kullanmanın ana nedenlerinden biri, yalnızca değişen öğeleri oluşturmak olduğu için, bindburadaki işlevleri kullanmak veya ok işlevlerini kullanmak kendinizi ayağınıza atmaktır. O edilir değil de özellikle çalışma durumunda olsa belgelenmiş mapping Listelerinde içinde diziler vs
hippietrail

"Her seferinde yeni işlev örnekleri oluşturmak, durumun değiştirildiği anlamına gelir" bununla ne demek istiyorsun?
Soruda

4

Aynı bağımsız değişkenlerle yeni işlevler oluşturmaktan kaçınmak için, işlev bağlama sonucunu hatırlayabilirsiniz, işte memobindbunu yapmak için basit bir yardımcı program : https://github.com/supnate/memobind


4

Bunun gibi satır içi işlevleri kullanmak gayet iyi. Linting kuralı geçerliliğini yitirmiştir.

Bu kural, ok işlevlerinin yaygın olmadığı ve insanların eskiden yavaş olan .bind (this) 'i kullandığı bir zamandır. Performans sorunu Chrome 49'da düzeltildi.

Satır içi işlevleri bir alt bileşene destek olarak aktarmamaya dikkat edin.

React Router'ın yazarı Ryan Florence bununla ilgili harika bir yazı yazdı:

https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578


Satır içi ok işlevlerine sahip bileşenler üzerinde bir birim testinin nasıl yazılacağını gösterebilir misiniz?
krankuba

1
@krankuba Bu soru bununla ilgili değildi. Satır içi olarak tanımlanmamış ancak yine de test edilemeyen anonim işlevleri yine de aktarabilirsiniz.
sbaechler

-1

Ok işlevlerini react-cached-handler kitaplığını kullanarak kullanabilirsiniz , yeniden işleme performansı konusunda endişelenmenize gerek yoktur:

Not: Dahili olarak, ok işlevlerinizi belirtilen tuşla önbelleğe alır, yeniden oluşturma konusunda endişelenmenize gerek yoktur!

render() {

  return <div>
  {
        this.props.photos.map(photo=>
          <Photo key={photo.url}
            onClick={this.handler(photo.url, (url) => { 
                 console.log(url) })}
          />)
   }
 </div>

}

Diğer özellikler:

  • Adlandırılmış işleyiciler
  • Olayları ok işlevleriyle işleyin
  • Anahtara, özel argümanlara ve orijinal olaya erişim
  • Bileşen oluşturma performansı
  • İşleyiciler için özel bağlam

Soru, onu neden kullanamayacağımızdı. Başka bir hack ile nasıl kullanılacağını değil.
kapil

-1

JSX props neden ok işlevlerini kullanmamalı veya bağlamamalı?

Çoğunlukla, satır içi işlevler optimize edilmiş bileşenlerin hafızaya alınmasını engelleyebileceği için:

Geleneksel olarak, React'teki satır içi işlevlerle ilgili performans endişeleri, her işlemede yeni geri çağırmaların geçmenin shouldComponentUpdatealt bileşenlerde optimizasyonları nasıl bozduğuyla ilgilidir . ( dokümanlar )

Ek işlev oluşturma maliyeti hakkında daha az şey var:

Function.prototype.bind Burada düzeltilen performans sorunları ve ok işlevleri ya yerel bir şeydir ya da babel tarafından düz işlevlere aktarılır; her iki durumda da yavaş olmadığını varsayabiliriz. ( React Eğitimi )

İşlev yaratmanın pahalı olduğunu iddia eden insanların her zaman yanlış bilgilendirildiğine inanıyorum (React ekibi bunu asla söylemedi). ( Tweet )

react/jsx-no-bindKural ne zaman işe yarar?

Hafızaya alınmış bileşenlerin amaçlandığı gibi çalıştığından emin olmak istersiniz:

  • React.memo (fonksiyon bileşenleri için)
  • PureComponentveya özel shouldComponentUpdate(sınıf bileşenleri için)

Bu kurala uyarak, kararlı işlev nesne başvuruları aktarılır. Bu nedenle, yukarıdaki bileşenler, önceki aksesuarlar değişmediğinde yeniden oluşturmayı engelleyerek performansı optimize edebilir.

ESLint hatası nasıl çözülür?

Sınıflar: İşleyiciyi yöntem olarak veya bağlama için sınıf özelliği olarak tanımlayın this.
Kancalar: Kullanın useCallback.

Orta yere

Çoğu durumda, satır içi işlevlerin kullanımı çok uygundur ve performans gereksinimleri açısından kesinlikle iyidir. Ne yazık ki, bu kural yalnızca hafızaya alınmış bileşen türleriyle sınırlanamaz. Yine de her yerde kullanmak istiyorsanız, örneğin basit DOM düğümleri için devre dışı bırakabilirsiniz :

rules: {
  "react/jsx-no-bind": [ "error", { ignoreDOMComponents: true } ],
}

const Comp = () => <span onClick={() => console.log("Hello!")} />; // no warning
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.