Как использовать функции стрелок (открытые поля класса) в качестве методов класса?

139

Я новичок в использовании классов 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.

Показать ещё 1 комментарий
Теги:
ecmascript-6
babeljs
ecmascript-next

5 ответов

172
Лучший ответ

Ваш синтаксис слегка выключен, просто отсутствует знак равенства после имени свойства.

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

Это экспериментальная функция. Вам нужно будет включить экспериментальные функции в Babel, чтобы получить это для компиляции. Здесь - демонстрация с включенной экспериментальной версией.

Чтобы использовать экспериментальные функции в babel, вы можете установить соответствующий плагин из здесь. Для этой конкретной функции вам нужен плагин transform-class-properties:

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

Подробнее о предложении для полей классов и статических свойств вы можете прочитать здесь


  • 4
    (Хотя я знаю, что это работает вне класса ES6), который, кажется, не работает для меня, babel бросает неожиданную стрелку токена в первый раз = at handleInputChange =
  • 0
    Это странно, когда я читал об этом в блоге Babel, смотрите здесь.
Показать ещё 11 комментариев
50

Нет, если вы хотите создать связанные, специфичные для экземпляра методы, вам нужно будет сделать это в конструкторе. Однако для этого вы можете использовать функции стрелок, вместо использования .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);
  }
}
  • 1
    Может ли кто-нибудь говорить о влиянии производительности на выполнение этого способа по сравнению с привязкой к прототипу?
  • 0
    @TylerCollier: не должно быть большой разницы. Если вам нужна абсолютная скорость, ни один из двух вариантов не является хорошим вариантом на самом деле. Но afaik bind незначительно медленнее, чем замыкания, потому что для этого требуется дополнительная уборка. В любом случае функции со стрелками более идиоматичны, начиная с ES6, что должно быть достаточным основанием для их использования.
Показать ещё 1 комментарий
2

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

Предположим, что у меня есть этот класс с конструктором:

//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);
    }
    ...
}
  • 0
    Ваше решение хорошо, однако оно не охватывает все методы жизненного цикла, если вы не объявите их во втором аргументе. Например: shouldComponentUpdate и getSnapshotBeforeUpdate
0

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

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

Вопрос готов, но я добавлю несколько примеров использования для использования метода ES6 arrow.

Внутри методов обратного вызова:

let  arr = [1,2];
let arr1 = arr.map((val,index) => {
    return val * 2;
});

или,

let arr1 = arr.map(val => { // if one argument then no need of using ()
    return val * 2;
});

Примечание: внутренняя функция обратного вызова, если используется this, то здесь this ссылка на функцию обратного вызова вместо класса компонента.

Пример:

arr.map(function(val) {
    this.func(val); // Will not work
});

arr.map((val,index)=>{
    this.func(); // Will  work
});

Ещё вопросы

Сообщество Overcoder
Наверх
Меню