——在 React 组件中抛弃 .bind(this)
。
这将会成为历史。
假设你经常在使用 React,你或许会不得不写类似于 .bind(this)
的代码,当然,我知道:
幸好,JavaScript 有一些提案中的特性可以让 .bind(this)
成为我们的过去。
在我解释如何抛弃 .bind(this)
之前,我将会给你看一段简单的例子来展示他可以被用在哪里。我们希望渲染一个点击了之后就能改变文本的按钮。为了实现这个效果,我们会写一个像下面差不多一个意思的组件:
import React, { Component } from "react";
class ButtonWithBind extends Component {
constructor() {
super();
this.state = { toggle: false };
}
render() {
const toggle = this.state.toggle;
return (
<div>
<button onClick={this.toggleButton}>
{toggle ? "ON" : "OFF"}
</button>
</div>
);
}
toggleButton() {
this.setState(prevState => ({ toggle: !prevState.toggle }));
}
}
export default ButtonWithBind;
我们在构造器中中把 toggle
的开关状态设置为 false
。
此外,我们绑定了一个onClick
处理函数: toggleButton
函数,所以它会在按钮被点击的时候被调用。
我们也创造了一个简单的 toggleButton
函数,用以在被调用时改变状态。
棒极了,看上去我们准备好了。
如果我们直接去点击这个渲染出来的按钮,会得到一个下图一样的 TypeError
:
铛!它明明应该工作的?!
我们之所以得到一个错误是因为当 onClick 调用我们的 toggleButton
函数时,函数并没有定义。
通常,你会通过在 toggleButton
函数中绑定上 this
指针来修复这个问题,所以他看上去保持不变。让我们继续在构造函数上位函数上绑定上 this
:
this.toggleButton = this.toggleButton.bind(this);
添加上这个之后,我们的按钮组件看上去像这样:
import React, { Component } from "react";
class ButtonWithBind extends Component {
constructor() {
super();
this.state = { toggle: false };
this.toggleButton = this.toggleButton.bind(this);
}
render() {
const toggle = this.state.toggle;
return (
<div>
<button onClick={this.toggleButton}>
{toggle ? "ON" : "OFF"}
</button>
</div>
);
}
toggleButton() {
this.setState(prevState => ({ toggle: !prevState.toggle }));
}
}
export default ButtonWithBind;
试试吧,它应该可以正常运作了:
是的,没毛病。
现在,让我们开始抛弃这个讨人厌的 .bind(this)
,为了做这个,我们将要使用 JavaScript 的实验中的公有类字段(public class fields)特性。公有类字段特性可以让你在你们的类中使用箭头函数语法。
toggleButton = () => {
this.setState(prevState => ({ toggle: !prevState.toggle }));
}
一个箭头函数没有它自己的 this
,不过他使用的是封闭的执行上下文的 this
值。箭头函数在词法上绑定它们的上下文,所以 this
实际上指向最原始的上下文。如果你要进行命名,这也被叫做词法环境。
从根本上来说,这让我们省下了代码中的 .bind(this)
。
注意,这是 JS 中的一个实验中的特性,这意味着她还没有被 ECMAScript 的标准所采纳,不过让我们保持手指交叉,做个?。在它被采纳之前,你可以配置 babel,使用 babel-plugin-transform-class-properties 来转换它。
记住,这可能会影响两件事。第一件是内存和性能。当你使用类字段来定义一个函数时,你的方法将驻留在类的每个实例上,而不是在原型上,因为原本使用了 bind
方法。你可以通过阅读 Donavon West 的一篇精彩的文章来深入理解——Demystifying Memory Usage using ES6 React Classes
第二件事是通过使用公有类字段来影响你如何编写单元测试,你将不能使用组件原型来进行这样的函数打桩:
const spy = jest.spyOn(ButtonWithoutBind.prototype, 'toggleButton'); expect(spy).toHaveBeenCalled();
不将不得不寻找另一种方法来打桩方法,比如在 props
中传递 spy
或者检查状态的变化。
现在,让我们跳到我们在组件中如何使用公有类字段,并改变我们的 toggleButton
函数来丢掉 .bind(this)
。
import React, { Component } from "react";
class ButtonWithoutBind extends Component {
constructor() {
super();
this.state = { toggle: false };
}
render() {
const toggle = this.state.toggle;
return (
<div>
<button onClick={this.toggleButton}>
{toggle ? "ON" : "OFF"}
</button>
</div>
);
}
toggleButton = () => {
this.setState(prevState => ({ toggle: !prevState.toggle }));
}
}
每个 React 开发者都曾经说过:看着 22 - 24 行,哇,太漂亮了!没有更多讨厌的
.bind(this)
了。
公有类字段还有一个好处,就是我们可以从构造函数中定义状态,然后细化我们的组件:
import React, { Component } from "react";
class ButtonWithoutBind extends Component {
state = { toggle: false }
render() {
const toggle = this.state.toggle;
return (
<div>
<button onClick={this.toggleButton}>
{toggle ? "ON" : "OFF"}
</button>
</div>
);
}
toggleButton = () => {
this.setState(prevState => ({ toggle: !prevState.toggle }));
}
}
export default ButtonWithoutBind;
而且,我们已经丢掉了 .bind(this)
,我们已经细化一丢丢我们的组件,可以称之为胜利了?!我们应该得到某种奖励,随意的去冰箱散步,拿一个冷的?或者一个巧克力?,又或者是任何你喜欢的东西。因为你刚刚学到了可以用在 React 中的全新的东西。
非常感谢 Kent C. Dodds 为此制作的视频。这篇文章没有他就不会存在。干杯? Kent。
如果你喜欢你所看到的,就请?并传播这个文章。另外,看看我的网站,follow 我,我将发布更多 React 相关的文章,所以点击 Follow 保持关注?。
如果你喜欢我的翻译文章,可以关注我的博客:https://codesky.me
扫码关注w3ctech微信公众号
共收到0条回复