Как использовать функции стрелок (открытые поля классов) в качестве методов класса?
Я новичок в использовании классов 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
объект.
4 ответа
Ваш синтаксис немного отключен, просто отсутствует знак равенства после имени свойства.
class SomeClass extends React.Component {
handleInputChange = (val) => {
console.log('selectionMade: ', val);
}
}
Это экспериментальная особенность. Вам нужно будет включить экспериментальные функции в Babel, чтобы компилировать это. Вот демоверсия с экспериментальной поддержкой.
Для использования экспериментальных функций в Babel вы можете установить соответствующий плагин здесь. Для этой конкретной функции вам нужно transform-class-properties
плагин:
{
"plugins": [
"transform-class-properties"
]
}
Вы можете прочитать больше о предложении для полей классов и статических свойств здесь
Нет, если вы хотите создать связанные, специфичные для экземпляра методы, вам придется сделать это в конструкторе. Однако вы можете использовать для этого функции стрелок вместо .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);
}
}
Я знаю, что на этот вопрос был дан достаточный ответ, но у меня есть небольшой вклад (для тех, кто не хочет использовать экспериментальную функцию). Из-за проблемы привязки нескольких привязок функций в конструкторе и придания ему запутанности, я пришел к вспомогательному методу, который после привязки и вызова в конструкторе автоматически выполняет все необходимые привязки методов.
Предположим, у меня есть этот класс с конструктором:
//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);
}
...
}
Вы используете функцию стрелки, а также связываете ее в конструкторе. Так что вам не нужно делать привязку при использовании функций стрелок
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);
}
}
Также не рекомендуется связывать функцию непосредственно в рендере. Это всегда должно быть в конструкторе