在响应onClick事件时绑定'this'与箭头函数

Kyl*_*ley 14 javascript reactjs

所以我正在努力学习反应,并.bind(this)在构造函数中与理解混淆了一点.但是我想我现在明白了,只是想知道为什么我会在onClick中使用它与箭头函数.见下面的代码:

绑定方法确保eventClick函数中的'this'引用该类

Class Click extends react.Component {
  constructor(props) {
   super(props)
   this.clickEvent = this.clickEvent.bind(this);
  }

  render = () => (
    <button onClick={this.clickEvent}>Click Me</button>
  )

  clickEvent() {console.log(this)} // 'this' refers to the class
}
Run Code Online (Sandbox Code Playgroud)

但是这个方法也引用了这个类.是否有任何赞成/反对使用一个与另一个?

Class Click extends react.Component {

  render = () => (
    <button onClick={() => {this.clickEvent()}}>Click Me</button>
  )

  clickEvent() {console.log(this)} // 'this' refers to the class
}
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 16

您的第二个示例在每个示例上重新创建函数render.在第一个中,您只需创建一次绑定函数.

您可以在构造函数中创建处理程序作为箭头函数:

class Click extends react.Component {
  constructor(props) {
    super(props)
    this.clickEvent = () => {   // ***
      console.log(this);        // ***
    };                          // ***
  }

  render = () => (
    <button onClick={this.clickEvent}>Click Me</button>
  )
}
Run Code Online (Sandbox Code Playgroud)

使用类字段提议语法(在大多数React项目的转换器设置中启用,以及您为render函数使用的语法),您可以这样写:

class Click extends react.Component {
  constructor(props) {
    super(props)
  }

  clickEvent = () => {          // ***
    console.log(this);          // ***
  };                            // ***

  render = () => (
    <button onClick={this.clickEvent}>Click Me</button>
  )
}
Run Code Online (Sandbox Code Playgroud)

这是一回事.


附注:您正在render为类的每个实例创建一个单独的函数.没有必要这样做,它可以在原型上.所以:

class Click extends react.Component {
  constructor(props) {
    super(props)
  }

  clickEvent = () => {
    console.log(this);
  };

  render() {
    return <button onClick={this.clickEvent}>Click Me</button>;
  }
}
Run Code Online (Sandbox Code Playgroud)


Top*_*ter 10

首先,让我们看一下每种技术的示例。

捆绑:

import React from 'react';
class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.clickHandler = this.clickHandler.bind(this);
  }

  clickHandler() {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}
Run Code Online (Sandbox Code Playgroud)

箭头功能:

import React from 'react';
class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }

  clickHandler = () => {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}
Run Code Online (Sandbox Code Playgroud)

优点和缺点:

由于较少的代码行,所以在公共类字段上使用Arrow函数更易于阅读,但是请记住,使用Arrow函数可能会影响两件事:

首先是  记忆和表现 ; 当您使用类字段定义函数时,整个方法都驻留在  该类的每个实例上,  而不是原型上,但是使用  绑定技术,callback每个实例上只存储一小部分,这将调用存储在该实例上的方法原型。

可能受影响的第二件事是如何编写单元测试。您将无法使用组件原型对以下函数调用进行存根:

const spy = jest.spyOn(MyComponent.prototype, 'clickHandler');
expect(spy).toHaveBeenCalled();
Run Code Online (Sandbox Code Playgroud)

您将必须找到另一种方法来存根该方法,方法是  将间谍传递给props  或  检查状态更改

结论

计算机真的很擅长阅读代码。您不必为此担心。您可能需要考虑使用类属性箭头功能使代码更易于阅读。


但是,如果要同时保持人类可读性和性能,请考虑使用plugin-transform-arrow-functions插件(尽管这对我v7.2.0 造成了问题),只需运行npm i --save-dev @babel/plugin-transform-arrow-functions并将其添加到“ babel.config.js”或“ .babelrc”文件中即可,例如:

{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": false }],
    ["@babel/plugin-transform-arrow-functions", { "spec": true }]
  ]
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用类似auto-bind decorator的东西,它将上面的示例转换为:

import React from 'react';
import { boundMethod as bind } from 'autobind-decorator';
class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }

  @bind
  clickHandler() {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:不必@bind使用所有功能。您只需要绑定传递的函数。例如onClick={this.doSomething}fetch.then(this.handleDone)