アロー関数(パブリッククラスフィールド)をクラスメソッドとして使用する方法


181

ReactでES6クラスを使用するのは初めてですが、以前は現在のオブジェクトにメソッドをバインドしていましたが(最初の例を参照)、ES6を使用すると、クラス関数を矢印でクラスインスタンスに永続的にバインドできますか?(コールバック関数として渡すときに便利です。)CoffeeScriptでできるように使用しようとすると、エラーが発生します。

class SomeClass extends React.Component {

  // Instead of this
  constructor(){
    this.handleInputChange = this.handleInputChange.bind(this)
  }

  // Can I somehow do this? Am i just getting the syntax wrong?
  handleInputChange (val) => {
    console.log('selectionMade: ', val);
  }

SomeClass.handleInputChangeたとえばsetTimeout、に渡した場合、スコープはwindowオブジェクトではなくクラスインスタンスにスコープされます。


1
私はのために同じ答えを知ることに興味がある活字体
火星ロバートソン

TypeScriptのソリューションは、ES7の提案と同じです(受け入れられた回答を参照)。これは、TypeScriptでネイティブにサポートされています。
フィリップブリー16


1
アロー関数はプロトタイプの一部ではないため、すべてのインスタンスで共有されないため、クラスでのアロー関数の使用は避けてください。これは、すべてのインスタンスに同じ機能のコピーを与えることと同じです。
Sourabh Ranka

回答:


205

構文は少しずれています。プロパティ名の後に等号が欠けているだけです。

class SomeClass extends React.Component {
  handleInputChange = (val) => {
    console.log('selectionMade: ', val);
  }
}

これは実験的な機能です。これをコンパイルするには、Babelの試験的な機能を有効にする必要があります。ここでは、実験が有効でデモがあります。

babelで実験的な機能を使用するには、ここから関連するプラグインをインストールできます。この特定の機能には、transform-class-propertiesプラグインが必要です。

{
  "plugins": [
    "transform-class-properties"
  ]
}

クラスフィールドと静的プロパティの提案について詳しくは、こちらをご覧ください。



4
私のために仕事には表示されません(私は外ES6クラスの作品を知っているけれども)、バベルは、最初に予期しないトークン矢印をスロー=handleInputChange =
ベン

40
たとえば、これはES7提案の実験的な機能であるなど、いくつかの説明を提供する必要があります。
Felix Kling、2015

1
現在の仕様ドラフトは9月に変更されたため、Babelが提案するように、それを自動バインドに使用しないでください。
chico

1
Babel 6.3.13の場合、これをコンパイルするには、プリセット「es2015」と「stage-1」をアクティブ化する必要があります
Andrew

12
動作しますが、メソッドはプロトタイプに追加されるのではなく、コンストラクターのインスタンスに追加されます。これは大きな違いです。
lib3d

61

いいえ、バインドされたインスタンス固有のメソッドを作成する場合は、コンストラクターで作成する必要があります。ただし、.bindプロトタイプメソッドではなく、矢印関数を使用できます。

class SomeClass extends React.Component {
  constructor() {
    super();
    this.handleInputChange = (val) => {
      console.log('selectionMade: ', val, this);
    };
    
  }
}

を省略して同じ機能でクラススコープに直接割り当てることができる提案がありますが、これはconstructor()非常に実験的なものであるため、使用しないことをお勧めします。

または、常にを使用することもできます.bind。これにより、プロトタイプでメソッドを宣言し、コンストラクターのインスタンスにバインドできます。このアプローチでは、クラスの外側からメソッドを変更できるため、柔軟性が高くなります。

class SomeClass extends React.Component {
  constructor() {
    super();
    this.handleInputChange = this.handleInputChange.bind(this);
    
  }
  handleInputChange(val) {
    console.log('selectionMade: ', val, this);
  }
}

4

この質問は十分に回答されていると思いますが、(実験的な機能を使用したくない人のために)わずかな貢献しかありません。コンストラクターで複数の関数バインドをバインドする必要があり、乱雑に見えるという問題があるため、一度バインドしてコンストラクターで呼び出すと、必要なすべてのメソッドバインディングが自動的に実行されるユーティリティメソッドが思い付きました。

このクラスにコンストラクターがあると仮定します。

//src/components/PetEditor.jsx
import React from 'react';
class PetEditor extends React.Component {
  
   constructor(props){
        super(props);
        this.state = props.currentPet || {tags:[], photoUrls: []};
     
        this.tagInput = null;
        this.htmlNode = null;

        this.removeTag = this.removeTag.bind(this);
        this.handleChange = this.handleChange.bind(this);
        this.modifyState = this.modifyState.bind(this);
        this.handleKeyUp = this.handleKeyUp.bind(this);
        this.addTag = this.addTag.bind(this);
        this.removeTag = this.removeTag.bind(this);
        this.savePet = this.savePet.bind(this);
        this.addPhotoInput = this.addPhotoInput.bind(this);
        this.handleSelect = this.handleSelect.bind(this);
        
    }
    // ... actual method declarations omitted
}

乱雑に見えますね。今、私はこのユーティリティメソッドを作成しました

//src/utils/index.js
/**
 *  NB: to use this method, you need to bind it to the object instance calling it
 */
export function bindMethodsToSelf(objClass, otherMethodsToIgnore=[]){
    const self = this;
    Object.getOwnPropertyNames(objClass.prototype)
        .forEach(method => {
              //skip constructor, render and any overrides of lifecycle methods
              if(method.startsWith('component') 
                 || method==='constructor' 
                 || method==='render') return;
              //any other methods you don't want bound to self
              if(otherMethodsToIgnore.indexOf(method)>-1) return;
              //bind all other methods to class instance
              self[method] = self[method].bind(self);
         });
}

次に必要なのは、そのユーティリティをインポートして、コンストラクターへの呼び出しを追加することだけです。コンストラクターの新しい各メソッドをバインドする必要はもうありません。新しいコンストラクタは、次のようにきれいに見えます。

//src/components/PetEditor.jsx
import React from 'react';
import { bindMethodsToSelf } from '../utils';
class PetEditor extends React.Component {
    constructor(props){
        super(props);
        this.state = props.currentPet || {tags:[], photoUrls: []};
        this.tagInput = null;
        this.htmlNode = null;
        bindMethodsToSelf.bind(this)(PetEditor);
    }
    // ...
}


あなたの解決策は素晴らしいですが、2番目の引数で宣言しない限り、ライフサイクルメソッドのすべてを網羅しているわけではありません。例:shouldComponentUpdateandgetSnapshotBeforeUpdate
WebDeg Brian

あなたの考えは自動バインディングに似ており、パフォーマンスが著しく低下します。渡す関数をバインドするだけで済みます。medium.com/@charpeni/…を
Michael Freidgeim

3

アロー関数を使用しており、コンストラクターでそれもバインドしています。したがって、矢印関数を使用するときにバインディングを行う必要はありません

class SomeClass extends React.Component {
  handleInputChange = (val) => {
    console.log('selectionMade: ', val);
  }
}

または、以下のような通常の関数を使用する場合、コンストラクタでのみ関数をバインドする必要があります

class SomeClass extends React.Component {
   constructor(props){
      super(props);
      this.handleInputChange = this.handleInputChange.bind(this);
   }

  handleInputChange(val){
    console.log('selectionMade: ', val);
  }
}

また、レンダリングで関数を直接バインドすることはお勧めしません。常にコンストラクタ内にある必要があります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.