所以我正在尝试学习
JavaScript
和/或 react
并且在构造函数中对 .bind(this)
的理解有点混乱。不过,我现在想明白了,只想知道,JavaScript
中使用绑定与箭头函数?onClick
事件中)。
请参阅下面的代码示例。
绑定方法确保
this
在clickEvent
函数引用类:
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
}
但是下面的方法也引用了类:
class Click extends react.Component {
render = () => (
<button onClick= {() => { this.clickEvent() }}> Click Me < /button>
)
clickEvent() { console.log(this) } // 'this' refers to the class
}
首先,让我们从每个技术的例子开始吧!
但是区别更多的是
JavaScript
语言本身。
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>
}
}
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>
}
}
在 public-class-field 上使用 Arrow-function 更具人类可读性, 因为更少的代码行, 但请记住,使用箭头函数会影响两件事:
首先是内存和性能;当你使用一个类字段来定义一个函数时,你的整个方法驻留在类的每个实例而不是原型上,但是使用bind技术,只有一个小的
callback
存储在每个实例上,这调用存储在原型上的方法。
第二个可能受到影响的是你如何编写你的单元测试。 您将无法使用组件原型来存根函数调用,如下所示:
const spy = jest.spyOn(MyComponent.prototype, 'clickHandler');
// ...
expect(spy).toHaveBeenCalled();
必须找到另一种方法来存根该方法,例如,通过 在
props
中传递虚拟回调,或者 检查state
更改.
计算机真的很擅长阅读代码;你不应该为此担心。 您可能需要考虑使用类属性箭头函数使您的代码更易于阅读。
如果你想保持人类可读性和性能,考虑使用 plugin-transform-arrow-functions 插件,只需运行
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 }]
]
}
在哪里
要求插件自动转换箭头功能, 进入方法/函数绑定(在"spec": true
中)。prototype
此外,似乎说插件是not与react-native兼容(至少链接帖子中提到的版本)。
或者, 你可以使用像 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>
}
}
注意没有必要在每个函数上都加上
。你只需要绑定你传递的函数。例如@bind
或onClick={this.doSomething}
fetch.then(this.handleDone)
您的第二个示例在每个
render
上重新创建包装函数。首先,您只创建一次原型函数,并在构造函数中为每个实例只创建一次绑定函数。
作为替代方案,您可以在构造函数中将处理程序创建为箭头函数:
class Click extends React.Component {
constructor(props) {
super(props);
this.clickEvent = () => { // ***
console.log(this); // ***
}; // ***
}
render = () => (
<button onClick={this.clickEvent}>Click Me</button>
);
}
无论你这样做还是使用
bind
,这在很大程度上是一个风格问题。 (我使用bind
所以函数在原型上所以我们可以模拟它用于测试等目的。)
使用 class fields proposal syntax(在大多数 React 项目的转译器设置中启用,并且您正在将其用于您的
render
函数),您可以这样写:
class Click extends React.Component {
constructor(props) {
super(props);
}
clickEvent = () => { // ***
console.log(this); // ***
}; // ***
render = () => (
<button onClick={this.clickEvent}>Click Me</button>
);
}
这是一回事。为关闭实例的每个实例创建一个单独的
clickEvent
函数。上面的两个例子做了完全相同的事情(在构造函数中调用 super()
之后创建函数并将其分配给实例),唯一的区别是语法。
旁注:您正在为类的每个实例创建一个单独的
render
函数。没有必要这样做,它可以在原型上。所以:
class Click extends React.Component {
constructor(props) {
super(props);
}
clickEvent = () => {
console.log(this);
};
render() {
return <button onClick={this.clickEvent}>Click Me</button>;
}
}