如何将箭头函数(公共类字段)用作类方法?

216

我是第一次使用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);
  }

因此,如果我将 SomeClass.handleInputChange 传递给例如 setTimeout ,它将作用于类实例范围内,而不是 window 对象。


2
我对了解TypeScript的相同答案很感兴趣。 - Mars Robertson
TypeScript 的解决方案与 ES7 提案相同(请参见被接受的答案)。这在 TypeScript 中得到本地支持。 - Philip Bulley
4
这篇文章讨论了“类属性中的箭头函数可能并不像我们想象中那样好”的话题,对于所有来到这里的人来说,这是一篇有趣的阅读材料。原文链接如下:"arrow functions in class properties might not be as great as we think" - Wilt
2
在类中应避免使用箭头函数,因为它们不是原型的一部分,因此不会被每个实例共享。这与将相同的函数副本提供给每个实例相同。 - Sourabh Ranka
2
@SourabhRanka 在构造函数中添加this绑定也可以实现这个吧? - user16037629
你在函数名后面缺少了一个等号。handleInputChange = (val) => { console.log('selectionMade: ', val); } - Aamer Shahzad
4个回答

247
您的语法略有偏差,属性名称后只是缺少等号。
class SomeClass extends React.Component {
  handleInputChange = (val) => {
    console.log('selectionMade: ', val);
  }
}

这是一个实验性功能。您需要在Babel中启用实验性功能才能进行编译。这里有一个启用了实验性功能的演示。

要在babel中使用实验性功能,您可以从这里安装相关插件。对于这个特定的功能,您需要transform-class-properties插件

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

你可以在这里阅读有关类字段和静态属性提案的更多信息。

4
尽管我知道在ES6类之外可以使用这种方法,但它似乎对我不起作用。Babel会在第一个=处的handleInputChange =抛出意外的箭头标记错误。 - Ben
42
您应该提供一些解释,例如这是一个ES7提案的实验性功能。 - Felix Kling
1
当前规范草案在九月份进行了更改,因此您不应该像Babel建议的那样将其用于自动绑定。 - chico
1
对于 Babel 6.3.13 版本,您需要激活 'es2015' 和 'stage-1' 预设才能编译此代码。 - Andrew
14
它能够工作,但该方法是在构造函数中添加到实例而不是添加到原型上,这是一个很大的区别。 - lib3d
显示剩余9条评论

71

不,如果你想创建绑定到特定实例的方法,你必须在构造函数中完成。然而,你可以使用箭头函数来代替在原型方法上使用.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);
  }
}

6

我知道这个问题已经得到了充分的回答,但是我有一个小贡献要做(为那些不想使用实验性功能的人)。由于在构造函数中绑定多个函数绑定的问题,并使其看起来混乱,我想出了一个实用方法,一旦在构造函数中绑定并调用,就会自动为您完成所有必要的方法绑定。

假设我有这个带有构造函数的类:

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


你的解决方案很好,但是它没有覆盖所有的生命周期方法,除非你在第二个参数中声明它们。例如:shouldComponentUpdategetSnapshotBeforeUpdate - WebDeg Brian
你的想法类似于自动绑定,这会导致明显的性能下降。你只需要绑定传递的函数即可。请参阅 https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1 - Michael Freidgeim

5

您正在使用箭头函数,并在构造函数中进行绑定。因此,当您使用箭头函数时,无需进行绑定。

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

当您使用普通函数时,您需要仅在构造函数中绑定函数,如下所示:

OR 您需要仅在构造函数中绑定函数,当您使用普通函数时

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

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

在渲染时直接绑定函数是不推荐的。它应该总是在构造函数中绑定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接