Reactでイベントを処理する新しい方法
- 「プロパティ初期化構文」*は実際よりも派手に聞こえます。 この一口サイズのチュートリアルでは、イベントハンドラーを作成するこの代替方法が、
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ドキュメント
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
になります。 これは、バベルが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をチェックして、プロパティ初期化構文の動作を確認してください