CodeSky 代码之空

随手记录自己的学习过程

[翻译]在 React 中抛弃 .bind(this)

2018-02-02 20:30分类: JavaScript评论: 5

原文:Losing .bind(this) in React

——在 React 组件中抛弃 .bind(this)

这将会成为历史。

假设你经常在使用 React,你或许会不得不写类似于 .bind(this) 的代码,当然,我知道:

  • 这看上去真的丑
  • 这在代码中占用了额外的空间

幸好,JavaScript 有一些提案中的特性可以让 .bind(this) 成为我们的过去。

在我解释如何抛弃 .bind(this) 之前,我将会给你看一段简单的例子来展示他可以被用在哪里。我们希望渲染一个点击了之后就能改变文本的按钮。为了实现这个效果,我们会写一个像下面差不多一个意思的组件:

1import React, { Component } from "react";
2
3class ButtonWithBind extends Component {
4  constructor() {
5    super();
6
7    this.state = { toggle: false };
8  }
9
10  render() {
11    const toggle = this.state.toggle;
12
13    return (
14      <div>
15        <button onClick={this.toggleButton}>
16          {toggle ? "ON" : "OFF"}
17        </button>
18      </div>
19    );
20  }
21
22  toggleButton() {
23    this.setState(prevState => ({ toggle: !prevState.toggle }));
24  }
25}
26
27export default ButtonWithBind;
28

我们在构造器中中把 toggle 的开关状态设置为 false

此外,我们绑定了一个onClick 处理函数: toggleButton 函数,所以它会在按钮被点击的时候被调用。

我们也创造了一个简单的 toggleButton 函数,用以在被调用时改变状态。

棒极了,看上去我们准备好了。

如果我们直接去点击这个渲染出来的按钮,会得到一个下图一样的 TypeError

铛!它明明应该工作的?!

我们之所以得到一个错误是因为当 onClick 调用我们的 toggleButton 函数时,函数并没有定义。

通常,你会通过在 toggleButton 函数中绑定上 this 指针来修复这个问题,所以他看上去保持不变。让我们继续在构造函数上位函数上绑定上 this

1this.toggleButton = this.toggleButton.bind(this);
2

添加上这个之后,我们的按钮组件看上去像这样:

1import React, { Component } from "react";
2
3class ButtonWithBind extends Component {
4  constructor() {
5    super();
6
7    this.state = { toggle: false };
8
9    this.toggleButton = this.toggleButton.bind(this);
10  }
11
12  render() {
13    const toggle = this.state.toggle;
14
15    return (
16      <div>
17        <button onClick={this.toggleButton}>
18          {toggle ? "ON" : "OFF"}
19        </button>
20      </div>
21    );
22  }
23
24  toggleButton() {
25    this.setState(prevState => ({ toggle: !prevState.toggle }));
26  }
27}
28
29export default ButtonWithBind;
30

试试吧,它应该可以正常运作了:

是的,没毛病。

? .bind(this)

现在,让我们开始抛弃这个讨人厌的 .bind(this),为了做这个,我们将要使用 JavaScript 的实验中的公有类字段(public class fields)特性。公有类字段特性可以让你在你们的类中使用箭头函数语法。

1toggleButton = () => { 
2  this.setState(prevState => ({ toggle: !prevState.toggle }));
3}
4

一个箭头函数没有它自己的 this,不过他使用的是封闭的执行上下文的 this 值。箭头函数在词法上绑定它们的上下文,所以 this 实际上指向最原始的上下文。如果你要进行命名,这也被叫做词法环境。

从根本上来说,这让我们省下了代码中的 .bind(this)

注意,这是 JS 中的一个实验中的特性,这意味着她还没有被 ECMAScript 的标准所采纳,不过让我们保持手指交叉,做个?。在它被采纳之前,你可以配置 babel,使用 babel-plugin-transform-class-properties 来转换它。

可能的陷阱

记住,这可能会影响两件事。第一件是内存和性能。当你使用类字段来定义一个函数时,你的方法将驻留在类的每个实例上,而不是在原型上,因为原本使用了 bind 方法。你可以通过阅读 Donavon West 的一篇精彩的文章来深入理解——Demystifying Memory Usage using ES6 React Classes

第二件事是通过使用公有类字段来影响你如何编写单元测试,你将不能使用组件原型来进行这样的函数打桩:

1const spy = jest.spyOn(ButtonWithoutBind.prototype, 'toggleButton'); expect(spy).toHaveBeenCalled();
2

不将不得不寻找另一种方法来打桩方法,比如在 props 中传递 spy 或者检查状态的变化。

在组件中使用

现在,让我们跳到我们在组件中如何使用公有类字段,并改变我们的 toggleButton 函数来丢掉 .bind(this)

1
2import React, { Component } from "react";
3
4class ButtonWithoutBind extends Component {
5  constructor() {
6    super();
7
8    this.state = { toggle: false };
9  }
10
11  render() {
12    const toggle = this.state.toggle;
13
14    return (
15      <div>
16        <button onClick={this.toggleButton}>
17          {toggle ? "ON" : "OFF"}
18        </button>
19      </div>
20    );
21  }
22
23  toggleButton = () => {
24    this.setState(prevState => ({ toggle: !prevState.toggle }));
25  }
26}
27

每个 React 开发者都曾经说过:看着 22 - 24 行,哇,太漂亮了!没有更多讨厌的 .bind(this) 了。

公有类字段还有一个好处,就是我们可以从构造函数中定义状态,然后细化我们的组件:

1import React, { Component } from "react";
2
3class ButtonWithoutBind extends Component {
4  state = { toggle: false }
5  
6  render() {
7    const toggle = this.state.toggle;
8
9    return (
10      <div>
11        <button onClick={this.toggleButton}>
12          {toggle ? "ON" : "OFF"}
13        </button>
14      </div>
15    );
16  }
17
18  toggleButton = () => {
19    this.setState(prevState => ({ toggle: !prevState.toggle }));
20  }
21}
22
23export default ButtonWithoutBind;
24

而且,我们已经丢掉了 .bind(this),我们已经细化一丢丢我们的组件,可以称之为胜利了?!我们应该得到某种奖励,随意的去冰箱散步,拿一个冷的?或者一个巧克力?,又或者是任何你喜欢的东西。因为你刚刚学到了可以用在 React 中的全新的东西。

非常感谢 Kent C. Dodds 为此制作的视频。这篇文章没有他就不会存在。干杯? Kent。

如果你喜欢你所看到的,就请?并传播这个文章。另外,看看我的网站,[follow 我](follow me),我将发布更多 React 相关的文章,所以点击 Follow 保持关注?。

评论 (3)

开发者头条2018年2月7日 10:21

感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/xkl037 欢迎点赞支持! 欢迎订阅《全栈大道宽又长》https://toutiao.io/subjects/77790

一横2018年2月6日 10:45

感觉就是利用箭头函数保留this的指向了...有个小疑问... toggleButton是不需要加const什么的来声明么...

敖天羽2018年2月6日 13:19

它是 Class properties 而不是 variable

ah2018年2月2日 21:53

我可以这样理解吗?用了公有类字段…在这个类里的箭头函数都可以不用bind去绑定了!?

敖天羽2018年2月3日 19:01

对 因为 this 的指向是正确的,不用额外的绑定