web-dev-qa-db-ja.com

Reactでの矢印関数の正しい使用

バベルとWebpackでReactJSを使用し、矢印関数にES6と proposed class fields を使用しています。 各レンダリングの関数を再作成しない コンストラクターでのバインディングの動作と同様に、矢印関数により効率が向上することを理解しています。ただし、それらを正しく使用しているかどうかは100%わかりません。以下は、3つの異なるファイルのコードの簡略化されたセクションです。

私のコード:

Main.js

prevItem = () => {
    console.log("Div is clicked")
}

render(){
    return (
         <SecondClass prevItem={this.prevItem} />
    )
}

SecondClass.js

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

ThirdClass.js

<div onClick={()=>{this.props.onClick()}}>Previous</div>

質問:

上記のコードは矢印関数を正しく使用していますか? SecondClass.jsには、次のものも使用できることに気付きました。

<ThirdClass type="prev" onClick={this.props.prevItem} />

元の関数定義でES6矢印関数を使用したため、1つのメソッドと他のメソッドに違いはありますか?または、最後のdivまでずっと矢印構文を使用する必要がありますか?

31
kojow7

矢印関数は、コンストラクターでのバインディングがどのように機能するかと同様に、それぞれがレンダリングする関数を再作成しないことで物事をより効率的にすることを理解しています。

本当じゃない。矢印機能を使用している場所によって異なります。 Arrow functionがrenderメソッドで使用される場合、everytimeが機能する方法と同様に、bind renderが新しいインスタンスを作成します。この例を考えてください

<div onClick={()=>{this.onClick()}}>Previous</div>

ここでは、レンダーが呼び出されるたびに匿名関数が作成され、その関数が呼び出されると、this.onClickが呼び出されます。

ただし、以下の場合を検討してください

onClick = () => {
    console.log("Div is clicked")
}

上記の場合、アロー関数は毎回関数を再作成するのではなく、クラスがインスタンス化されるときにコンテキストをReactコンポーネントにAn arrow function does not have its own this; the this value of the enclosing execution context is used.として一度バインドします。これは、binding works is constructorに似ています。これはproposed class fields for arrow functionsの一部であり、ES6の機能ではありません。

尋ねたいことを理解するには、関数が呼び出された場所からコンテキストを取得することを知っておく必要があります。詳細については this question を確認してください。

あなたの場合は、Arrow functionを使用してprevItemを定義しているため、囲んでいるReactコンポーネントのコンテキストを取得します。

prevItem = () => {
    console.log("Div is clicked")
}

render(){
    return (
         <SecondClass prevItem={this.prevItem} />
    )
}

子では、カスタムコンテキストでprevItemを呼び出しても、using bind or arrow functionprevItemは親で実行された場合、つまりMain.jsがその囲みReactコンポーネントのコンテキストを取得します。そしてprevItem関数を実行したいだけで、子からこれにデータを渡したくないので、

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

そして

<div onClick={()=>{this.props.onClick()}}>Previous</div>

単に役に立たず、新しい関数がSecondClassおよびThirdClassで毎回作成されるため、パフォーマンスへの影響が増えるだけです。これらの関数を矢印関数として定義する必要はなく、単に書くことができます

<ThirdClass type="prev" onClick={this.props.prevItem} />

そして

<div onClick={this.props.onClick}>Previous</div>

既に親にバインドされているためです。

これで、ThirdClassおよびSecondClassからこれらの関数にいくつかの追加データを渡す必要がある場合でも、Arrow functionまたはbind in renderを直接使用しないでください。 How to Avoid binding in Render method でこの回答をご覧ください

29
Shubham Khatri

だからあなたの最初のアプローチ

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

これで、ThirdClassで使用可能な引数をprevItem関数に渡すことができます。これは、引数を使用して親関数を呼び出すのに適した方法です。

<ThirdClass type="prev" onClick={()=>this.props.prevItem(firstArgument, secondArgument)} />

2番目のアプローチは

<ThirdClass type="prev" onClick={this.props.prevItem} />

このアプローチでは、ThirdClass固有の引数を渡すことができません。

両方のアプローチは正しいです、それだけで、それはユースケースに依存します。 es6矢印関数を使用するアプローチと上記の各シナリオで正しいアプローチの両方

6
simbathesailor

JavaScriptカーリング関数宣言を使用すると、他の回答とは異なる方法になる可能性があります。次のコードに注意してください。

clickHandler = someData => e => this.setState({
  stateKey: someData
});

JSXに次のように記述できます。

<div onClick={this.clickHandler('someData')} />

clickHandlersomeDataは、e引数を持つ関数を返しますが、clickHandler関数内では使用されません。それでうまくいきます。

より完全に書くには、以下のように書きます:

clickHandler = someData => () => this.setState({
  stateKey: someData
});

eである必要はないので、なぜそれを書くべきなのか。

4
AmerllicA

元の関数定義で矢印を使用すると、コンストラクターで関数をバインドできなくなります。

矢印を使用しなかった場合...

prevItem(){
  console.log("Div is clicked")
}

次に、そこにバインドするコンストラクタを作成する必要があります...

class MyComponent extends Component {
  constructor(props) {
    super(props)
    this.prevItem = this.prevItem.bind(this)
  }

  prevItem() { ... }
}

矢印は簡単に機能し、コンストラクタが何であるかを理解したり、javascriptのthisの複雑さを掘り下げたりする必要がないため、開始時に簡単に使用できます。

ただし、パフォーマンスに関しては、コンストラクターでバインドする方が適切です。 bind in constructorメソッドは、renderメソッドが複数回呼び出された場合でも、関数の単一のインスタンスを作成して再利用します。

3
Josh Pittman