JSXプロップが矢印関数またはバインドを使用しないのはなぜですか?


103

Reactアプリでlintを実行していますが、次のエラーが表示されます。

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

そして、これは私が矢印関数を実行しているところです(内部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>
))}

これは避けるべき悪い習慣ですか?そしてそれを行うための最良の方法は何ですか?

回答:


170

JSXプロップでインライン矢印関数を使用すべきでない理由

JSXで矢印関数またはバインディングを使用することは、レンダリングごとに関数が再作成されるため、パフォーマンスを低下させる悪い習慣です。

  1. 関数が作成されるたびに、前の関数はガベージコレクションされます。多くの要素をレンダリングすると、アニメーションにジャンクが生じる可能性があります。

  2. インライン矢印機能を使用すると発生しますPureComponentSを、使用することをコンポーネントshallowCompareでのshouldComponentUpdate方法は、とにかく再度レンダリングします。アロー関数のプロップは毎回再作成されるため、浅い比較ではプロペラへの変更として識別され、コンポーネントが再レンダリングされます。

次の2つの例からわかるように、インライン矢印関数を使用すると、<Button>コンポーネントは毎回再レンダリングされます(コンソールには「レンダリングボタン」テキストが表示されます)。

例1- インラインハンドラーのない PureComponent

例2 - PureComponent インラインハンドラ

this矢印関数をインライン化せずにメソッドをバインドする

  1. コンストラクターでメソッドを手動でバインドします。

    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. 矢印関数で提案クラスフィールドを使用してメソッドをバインドします。これはステージ3の提案なので、ステージ3プリセットまたはClassプロパティ変換をバベル構成に追加する必要があります。

    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>
        );
      }
    }

内部コールバックを持つ関数コンポーネント

関数コンポーネント内に内部関数(たとえば、イベントハンドラー)を作成すると、コンポーネントがレンダリングされるたびに関数が再作成されます。関数が小道具として(またはコンテキストを介して)子コンポーネント(Buttonこの場合)に渡されると、その子も再レンダリングされます。

例1-内部コールバックを持つ関数コンポーネント:

この問題を解決するには、コールバックをuseCallback()フックでラップし、依存関係を空の配列に設定します。

注:useState生成された関数は、現在の状態を提供する更新機能を受け付けます。このように、現在の状態をの依存関係に設定する必要はありませんuseCallback

例2-useCallbackでラップされた内部コールバックを持つ関数コンポーネント:


3
ステートレスコンポーネントでこれをどのように実現しますか?
lux

4
ステートレス(関数)コンポーネントにはがthisないため、バインドするものはありません。通常、メソッドはラッパースマートコンポーネントによって提供されます。
Ori Drori

39
@OriDrori:コールバックでデータを渡す必要がある場合、どのように機能しますか?onClick={() => { onTodoClick(todo.id) }
adam-beck 2016年

4
@ adam-beck-クラスのコールバックメソッド定義内に追加しますcb() { onTodoClick(this.props.todo.id); }
Ori Drori 2016年

2
@ adam-beckこれはuseCallback動的な値で使用する方法だと思います。stackoverflow.com/questions/55006061/...
翔太田村

9

これは、JSXプロパティで使用した場合、矢印関数が各レンダリングで関数の新しいインスタンスを作成するためです。これはガベージコレクタに大きな負担をかける可能性があり、また、関数が再利用される代わりに破棄されるため、ブラウザが「ホットパス」を最適化するのを妨げます。

https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.mdで説明全体といくつかの詳細情報を確認できます


それだけでなく。毎回新しい関数インスタンスを作成することは、状態が変更されることを意味し、コンポーネントの状態が変更されると、コンポーネントは再レンダリングされます。Reactを使用する主な理由の1つは、変化する要素のみをレンダリングするためbindです。ここで、または矢印関数を使用すると、足元を撃ちます。それはされていないだけでなく、特にでの作業の場合には、かかわらず、文書mapのpingリスト内の配列など
ヒッピー・トレイル

「毎回新しい関数インスタンスを作成することは、状態が変更されることを意味します」それはどういう意味ですか?質問には州がまったくありません
apieceofbart


4

このようなインライン関数を使用しても問題ありません。リンティングルールが古くなっています。

このルールは、矢印関数が一般的でなく、人々が.bind(this)を使用していた時代からのものでした。パフォーマンスの問題はChrome 49で修正されました。

インライン関数を小道具として子コンポーネントに渡さないように注意してください。

React Routerの作者であるRyan Florenceは、これについて素晴らしい記事を書いています。

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


インライン矢印関数を使用してコンポーネントの単体テストを作成する方法を教えていただけますか?
krankuba

1
@krankubaこれはこの質問についてのことではありません。インラインで定義されていないがまだテストできない匿名関数を渡すこともできます。
sbaechler

-1

反応キャッシュハンドラーライブラリを使用して矢印関数を使用できます。再レンダリングのパフォーマンスについて心配する必要はありません。

注:内部的には、指定されたキーによって矢印関数をキャッシュします。再レンダリングについて心配する必要はありません。

render() {

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

}

その他の機能:

  • 名前付きハンドラー
  • 矢印関数でイベントを処理する
  • キー、カスタム引数、元のイベントへのアクセス
  • コンポーネントレンダリングのパフォーマンス
  • ハンドラーのカスタムコンテキスト

問題は、なぜそれを使用できないのかということでした。他のハックでそれを使用する方法ではありません。
カピル

-1

JSXプロップが矢印関数またはバインドを使用しないのはなぜですか?

ほとんどの場合、インライン関数は最適化されたコンポーネントのメモ化を破壊する可能性があるためです。

従来、Reactのインライン関数に関するパフォーマンスの問題は、各レンダーに新しいコールバックを渡すshouldComponentUpdateと子コンポーネントの最適化がどのように機能しなくなるかに関係していました。(ドキュメント

追加の関数作成コストは少なくなります。

Function.prototype.bind ここ修正されたパフォーマンスの問題と矢印関数はネイティブのものであるか、バベルによってプレーン関数に変換されます。どちらの場合も、遅くはないと想定できます。(リアクトトレーニング

関数の作成が高価であると主張する人々は常に誤解されてきたと思います(Reactチームはこれを決して言っていません)。(ツイート

react/jsx-no-bindルールはいつ役に立ちますか?

メモ化されたコンポーネントが意図したとおりに機能することを確認します。

  • React.memo (関数コンポーネント用)
  • PureComponentまたはカスタムshouldComponentUpdate(クラスコンポーネントの場合)

この規則に従うことにより、安定した関数オブジェクト参照が渡されます。したがって、上記のコンポーネントは、以前の小道具が変更されていない場合、再レンダリングを防止することでパフォーマンスを最適化できます。

ESLintエラーを解決するには?

クラス:メソッド、またはとしてハンドラを定義したクラスのプロパティのためのthisバインディング。
フック:使用useCallbackます。

ミドルグラウンド

多くの場合、インライン関数は非常に使いやすく、パフォーマンス要件の点で非常に優れています。残念ながら、このルールはメモ化されたコンポーネントタイプのみに限定できません。それを全面的に使用したい場合は、たとえば単純なDOMノードで無効にすることができます。

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

const Comp = () => <span onClick={() => console.log("Hello!")} />; // no warning
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.