*「プロパティ初期化構文」*は実際よりも派手に聞こえます。 この一口サイズのチュートリアルでは、イベントハンドラーを作成するこの代替方法が、constructorの定型文を排除し、レンダリングでの軽薄なメモリ使用を防ぐのにどのように役立つかを確認してください。

Facebookドキュメントでは、次のようにイベント処理が行われていることがわかります。

// option 1
class Thing extends React.Component {
  constructor() {
    this.handleSmthng = this.handleSmthng.bind(this)
  }
  render() {
    <input onChange={this.handleSmthng}/>
  }
  handleSmthng(e) {
    // ...
  }
}

ES6クラスは、thisスコープをhandleSmthngに自動的に付与しません。通常、this.setStateを呼び出すか、コンポーネント内の別のメソッドである「公式」を呼び出す必要があるためです。慣例では、コンストラクターで常にすべてのイベントハンドラーをバインドします。 これは機能しますが、すぐに定型コードのように感じることがあります。


// option 2
class Thing extends React.Component {
  render() {
    <button onClick={() => this.handleSmthng('foo')}>
      ADD
    </button>
  }
  handleSmthng(arg1) {
    // ...
  }
}

このパターンは、オンラインのReactチュートリアルで人気が高まっているようです。 thisコンテキストをhandleSmthngに渡し、コンストラクターの定型コードを回避します。 このコンポーネントには状態がないので、コンストラクターも必要ありません。 このアプローチの動機は正しいと思います…しかし、わずかなパフォーマンスコストがあります。

矢印関数を使用すると、JavaScriptで常に新しい参照が作成され、アプリのメモリ使用量が増加します。 JavaScriptではメモリは安価ですが、Reactではレンダリングにコストがかかります。 矢印関数を子コンポーネントに渡すと、その矢印関数は新しいデータであるため、子コンポーネントは無差別に再レンダリングされます。 これは、大規模なReactアプリケーションで60fpsと50fpsを取得することの違いを意味する可能性があります。

「…しかし、このコールバックが下位コンポーネントへの小道具として渡された場合、それらのコンポーネントは余分な再レンダリングを行う可能性があります。」 React Docs

tiddly winks

2つのバインド1つのクロージャ

1)ボイラープレートを回避し、2)余分な再レンダリングを引き起こさないイベントハンドラーを作成するためのはるかにクリーンな方法があります:プロパティ初期化構文! 派手な名前ですが、アイデアは本当に単純です…矢印関数を使用してイベントハンドラーを定義するだけです。 このような:

class TodoApp extends React.Component {
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          ADD
        </button>
        <input onChange={this.handleInput}/>
      </div>
    );
  }
  handleClick = () => {
    // "this"
  }
  handleInput = (e) => {
    // "this", "e"
  }
}

あなたは2つのハンドラーを定義しました、そしてそれは本当に素晴らしく見えます。 ボイラープレートはありません。 読みやすい。 リファクタリングが簡単…引数を渡したい場合:

class TodoApp extends React.Component {
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          ADD
        </button>
        <input onChange={this.handleInput}/>
        {
          this.state.todos.map((item) => {
            return (
              <li onClick={this.handleRemove(item.id)}>
                {item.text}
              </li>
            );
          });
        }
      </div>
    )
  }

  handleClick = () => {
    // "this"
  }

  handleInput = (e) => {
    // "this", "e"
  }

  handleRemove = (id) => (e) => {
    // "this", "e", "id"
  }
}

ブーム! レンダーメソッドやコンストラクターでbindを使用せずに、引数を渡すことができます。 すべてが本当に刺激的でスパンに見えます。

矢印関数を見ることに慣れていない場合、これはおそらく奇妙に見えます。 ES6では、太い矢印の構文により、1行のステートメントで中括弧を省略できることを覚えておいてください…その行にあるものはすべて暗黙的にreturnになります。 これは、BabelがhandleRemoveをトランスパイルする方法です。

handleRemove: function (item) {
  return function (e) {
    // "item" AND "e" 🌈
  }
}

プロパティ初期化構文を使用するようにBabelを構成するには、「transform-class-properties」プラグインまたは enablestage-2がインストールされていることを確認してください。 「create-react-app」を使用している場合…すでにあります!

レンダリングで「バインド」または矢印関数を使用しないように指示するESLintルールがあります

プロパティ初期化構文を使用する利点

Facebookがドキュメントでこのパターンを「公式に」承認していないのは、ステージ2 ES6がまだ完成していないため、プロパティイニシャライザーが非標準と見なされているためです。 ただし、create-react-appジェネレーターはすでにステージ2を有効にしているため、近い将来、プロパティイニシャライザーがイベントハンドラーを定義するための事実上のものになる可能性が非常に高くなります。

プロパティイニシャライザーに慣れ、ハンドラーメソッドを定義するためにそれらを使用し始めると、2つの注目すべき利点が得られます。

  • 書くボイラープレートが少ないコンストラクターでbindステートメントを書く必要がないのはとても良いことです。 これで、メソッドを定義するだけです-そしてそれだけです✨。 引数を渡す必要がある場合は、単一のクロージャでラップし、renderでそのハンドラー関数を呼び出すことを忘れないでください。 追加の利点として、イベントハンドラーを別の場所でリファクタリングする必要がある場合、カットペーストする場所は1つだけです。
  • メモリ使用量の削減renderで矢印関数を使用することは、コンポーネントのライフサイクル中に「設計上」のレンダリングが大量に発生するため、お勧めできません。 すべての矢印関数に新しいポインタを割り当てます。 renderの矢印機能を控えることで、ダイエット中のコンポーネントのメモリ使用量を維持できます。

このCodePenをチェックして、プロパティ初期化構文の動作を確認してください