web-dev-qa-db-ja.com

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

ES6クラスをReactで使用するのは初めてです。以前は自分のメソッドを現在のオブジェクトにバインドしていましたが(最初の例を参照)、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);
  }

したがって、たとえばsetTimeoutSomeClass.handleInputChangeを渡すと、そのスコープはクラスインスタンスになり、windowオブジェクトにはなりません。

162
Ben

あなたの文法は少し外れていて、プロパティ名の後に等号がないだけです。

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

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

Babelで実験的な機能を使うためには、 here から適切なプラグインをインストールすることができます。この機能のためには、 transform-class-propertiesプラグイン が必要です。

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

Class FieldsとStatic Propertiesの提案についてもっと読むことができます ここ


189
Guy

いいえ、インスタンス固有のバインドされたメソッドを作成したい場合は、コンストラクターでそれを行う必要があります。ただし、プロトタイプメソッドで.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);
  }
}
57
Bergi

私はこの質問が十分に答えられたことを知っています、しかし私は(実験的な機能を使いたくない人のために)するために少しの貢献をしているだけです。コンストラクタ内で複数の関数バインドをバインドしなければならず、それがめちゃくちゃに見えるという問題のために、一度コンストラクタ内でバインドして呼び出すと、必要なすべてのメソッドのバインドを自動的に行うユーティリティメソッドを思いつきました。

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

//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);
    }
    // ...
}
3
kennasoft

あなたは矢印関数を使っていて、それをコンストラクタで束縛しています。ですから、あなたが矢印関数を使うとき、あなたは束縛をする必要はありません。

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

また、レンダリングで関数を直接バインドすることはお勧めできません。それは常にコンストラクタにあるべきです

0
Hemadri Dasari