本文是使用ES6来编写React应用系列的第三篇,本文主要将讨论的是React类中方法的绑定问题。 本篇博文的主要目的是解决前一篇文章中的遗留问题。
Statement
- 作者: 景庄 ,Web开发者,主要关注JavaScript、Node.js、React、Docker等。
- 源码: 本文的源代码地址:https://github.com/wwsun/react-es6-tutorial
介绍
如果你阅读并尝试过前一篇文章中的 CartItem
组件的render方法 , 你可能会对其中使用 {this.increaseQty.bind(this)}
这样的语句感到困惑。 我们的直觉是直接使用 <button onClick={this.increaseQty} className="button success">+</button>
这样的代码。 如果你真的尝试这么做了,我们将会在浏览器的控制台中看到这样的一条错误:
Uncaught TypeError: Cannot read property 'setState' of undefined
如下图所示:
这是因为如果我们使用这种方式调用函数的话,此时的 this
并没有绑定到类本身,而是 undefined
。 这是JavaScript的默认行为。相反,如果你使用 React.createClass()
来创建组件的话, 所有的方法都会被自动绑定到对象的实例上。对一些开发者而言,这可能有点反直觉。
之所以没有使用自动绑定,是因为React开发组在决定实现组件对ES6类的支持时决定的。 如果你想了解更详细的原因,你可以通过这篇博文了解更多。
下面我们来进一步的讨论,如果你使用的是ES6类的话,如何在JSX中使用不同的方式调用类方法。
方法1:使用 Function.prototype.bind()
这也就是我们已经在前一篇博文中使用过的方法:
class CartItem extends React.Component { render() { <button onClick={this.increaseQty.bind(this)} className="button success">+</button> } }
由于ES6类中的任何一个方法都是普通的JavaScript函数,它从Function原型中继承了 bind()
方法。 因此如果我们需要在JSX中调用 increaseQty()
, this
将会指向我们的类实例。 你可以从 MDN文章 中获取更多的有关 Function.prototype.bind()
方法的内容。
方法2:在构造器中使用被定义的函数
我们还可以通过在类的构造器中解决函数绑定的问题,代码如下:
class CartItem extends React.Component { constructor(props) { super(props); this.increaseQty = this.increaseQty.bind(this); } render() { <button onClick={this.increaseQty} className="button success">+</button> } }
这样,你就无需在JSX中再使用 bind()
方法了,但这种方法的缺点是增加了构造器的代码,并且让构造器不再优美。
方法3:使用胖箭头函数 =>
和构造器
ES6胖箭头函数会在它们被调用的时候自动保存 this
上下文。我们可以通过这个特性来重新咋构造器中定义 increaseQty()
:
class CartItem extends React.Component { constructor(props) { super(props); this._increaseQty = () => this.increaseQty(); } render() { <button onClick={this._increaseQty} className="button success">+</button> } }
小结
在本文中,我们主要讨论几种使用ES6代码来解决绑定到React组件方法的可行方案。 总体而言,由于JavaScript语言的自身特性,我们很难有一个统一的优雅的解决方案, 因此,我更偏向于使用第一种方法,在JSX中进行手动绑定。当然,在未来,ECMAScript标准将有更加优雅的解决方案。 如果你感兴趣的话,可以阅读 这篇文章 。
References
- About autobinding in official React blog
- Auto binding, React and ES6 Classes
- Function Bind Syntax in Official Babel Blog
- Function.prototype.bind()