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)