Binding vs arrow-function (для реакции на событие onClick)

Поэтому я пытаюсь научиться реагировать и немного перепутался с пониманием .bind(this) в конструкторе. Однако я думаю, что сейчас понимаю, и просто хочу знать, почему я буду использовать это против функции стрелок в onClick. См. Следующий код:

Метод привязки гарантирует, что "this" в функции eventClick ссылается на класс

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
}

Однако этот метод также ссылается на класс. Есть ли pro/con для использования одного и другого?

Class Click extends react.Component {

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

  clickEvent() {console.log(this)} // 'this' refers to the class
}

Ответы

Ответ 1

Ваш второй пример воссоздает функцию для каждого render. Во-первых, вы создаете связанную функцию только один раз.

Вы можете просто создать обработчик в конструкторе как функцию стрелки:

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

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

Используя синтаксис предложения полей полей (который включен в настройках транспилятора большинства проектов React и которые вы используете для своей функции render), вы можете написать это следующим образом:

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

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

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

Это одно и то же.


Замечание: вы создаете отдельную функцию render для каждого экземпляра вашего класса. Там нет необходимости делать это, это может быть на прототипе. Так:

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

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

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

Ответ 2

Прежде всего, давайте рассмотрим пример каждого метода.

Переплет:

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

Arrow-функция:

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

Плюсы и минусы:

Использование функции Arrow в public-class-field более читабельно, из-за меньшего количества строк кода, Но имейте в виду, что использование функции Arrow может повлиять на две вещи:

Сначала о памяти и производительности; Когда вы используете поле класса для определения функции, весь ваш метод находится в каждом экземпляре класса, а НЕ в прототипе, но с использованием техники связывания, только небольшой callback сохраняется в каждом экземпляре, который вызывает ваш метод, который хранится в прототипе.

Второе, что может повлиять на то, как вы пишете свои юнит-тесты. Вы не сможете использовать прототип компонента, чтобы заглушить вызовы функций, как показано ниже:

const spy = jest.spyOn(MyComponent.prototype, 'clickHandler');
expect(spy).toHaveBeenCalled();

Вам нужно будет найти другой способ заглушить метод, передав шпиона в реквизит или проверив изменения состояния.

Заключение

Компьютеры действительно хороши в чтении кода; ты не должен беспокоиться об этом. Возможно, вы захотите сделать ваш код более понятным для человека, используя функцию стрелки свойства класса.


Но если вы хотите сохранить удобочитаемость и производительность, рассмотрите возможность использования плагина 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 }]
  ]
}

Вы также можете использовать что-то вроде декоратора автоматической привязки, что превратит приведенный выше пример в:

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

Примечание. Нет необходимости ставить @bind на каждую функцию. Вам нужно только связать функции, которые вы передаете. например onClick={this.doSomething} Или fetch.then(this.handleDone)