React 与 ES6 - 第三部分,为 React 类绑定方法(ES7 同理)
=
这篇文章,是我们探索在 ECMAScript6 和 ECMAScript7 中 React 的使用方法的系列文章的第三篇。
想看这个系列的其他文章,请点如下链接:
- React and ES6 - Part 1, Introduction into ES6 and React
- React and ES6 - Part 2, React Classes and ES7 Property Initializers
- React and ES6 - Part 3, Binding to methods of React class (ES7 included)
- React and ES6 - Part 4, React Mixins when using ES6 and React
- React and ES6 - Part 5, React and ES6 Workflow with JSPM
- React and ES6 - Part 6, React and ES6 Workflow with Webpack
React | JavaScript |
---|---|
这个文章中的相关代码片段,也可以在 GitHub 中找到
最后更新日期: 2016年6月18日,更新内容包含了 React15 以及 Babel6。
这个系列的旧文章里,有一篇讲到了“CartItem 渲染方法”,如果你看过的话,可能会对 {this.increaseQty.bind(this)}
这种写法有点疑惑。
如果我们在 ES6 的代码里,对同样的 demo 用 {this.increaseQty}
来绑定一个组件的事件处理函数,浏览器会报 Uncaught TypeError: Cannot read property 'setState' of undefined
错误:
这是因为在 ES6 中,函数的 this
绑定规则已经发生了变化,我们在调用 this
的时候,调用的并不是类本身,而是 undefined
。但是如果你在写 React 的时候用的是 React.createClass()
这种方法, React 会自动把所有类的方法的 this
绑定到对应的实例上。
在 React 组件开始支持用 ES6 class
来实现的时候,React 小组决定不再支持自动绑定。详细的原因,可以看这篇文章。
下面来看看在用 ES6 class
写 JSX 文件的时候,怎么给类的方法绑定 this
值。
1. 用 Function.prototype.bind()
如下:1
2
3
4
5export default class CartItem extends React.Component {
render() {
<button onClick={this.increaseQty.bind(this)} className="button success">+</button>
}
}
由于 ES6 中类的方法本质上是 JavaScript 函数,因此继承了来自于 Function 原型上的 bind()
方法。现在,再调用 JSX 里的 increaseQty()
方法的时候,this
就会指向类的实例。如果对 Function.prototype.bind() 有疑惑,可以看这篇 MDN 文章。
2. 在构造函数中进行绑定
1 | export default class CartItem extends React.Component { |
这样就不需要在 JSX 里用 bind()
方法了,但是增加了构造函数里的代码。
3. 用箭头函数以及构造函数
在 ES6 的箭头函数 被调用的时候,this
是函数执行的上下文。我们可以利用这个特性,在构造函数里重新定义 increaseQty()
:1
2
3
4
5
6
7
8
9
10
11export default class CartItem extends React.Component {
constructor(props) {
super(props);
this._increaseQty = () => this.increaseQty();
}
render() {
<button onClick={_this.increaseQty} className="button success">+</button>
}
}
(译注:是不是写错了)
4. 用箭头函数以及 ES2015+ 的类属性
除了上面提到的 3 种方法,还可以把箭头函数跟 ES2015+ 的类属性组合起来写:1
2
3
4
5
6
7
8export default class CartItem extends React.Component {
increaseQty = () => this.increaseQty();
render() {
<button onClick={this.increaseQty} className="button success">+</button>
}
}
哈哈,这次我们没有用长的构造函数代码来实现我们的需求了,而是巧妙的利用了类的属性初始化。
警告: 类属性现在还不是当前的 JavaScript 标准,但是可以用 Babel 的实验版本标记(也就是 stage 0)来解决这个问题。关于 Babel 的使用方法,可以查看 Babel
文档。
这个系列的文章 React and ES6 - Part 2, React Classes and ES7 Property Initializers 里就已经在用 stage 0 了,所以在这篇文章里,应该不是什么问题。
5. 用 ES2015+ 的函数绑定语法
最近 Babel 增加了一个语法糖,用 ::
来表示 Function.prototype.bind()
,这个内容的细节不再展开。当然,如果你想了解细节,有些人已经在 Babel 官方文章 里对这个作了很好的解释。
下面是用了 ES2015+ 绑定语法的代码:1
2
3
4
5
6
7
8
9
10
11
12export default class CartItem extends React.Component {
constructor(props) {
super(props);
this.increaseQty = ::this.increaseQty;
// line above is an equivalent to this.increaseQty = this.increaseQty.bind(this);
}
render() {
<button onClick={this.increaseQty} className="button success">+</button>
}
}
友情提示,这是一个实验特性,如果想用的话,先考虑一下风险问题。
6. 直接在 JSX 里用 ES2015+ 的函数绑定语法
直接在 JSX 里用 ES2015+ 的语法糖,就不用再写构造函数的代码了:1
2
3
4
5export default class CartItem extends React.Component {
render() {
<button onClick={::this.increaseQty} className="button success">+</button>
}
}
看看,代码写起来很简洁,但是,这样会导致每次子组件被渲染的时候,重新初始化一个函数(译注:有关这个问题的扩展阅读请看这里),所以性能上是存在问题的。如果你想用纯渲染函数(或者 ES2016 的类)的时候,这样写会导致更严重的问题。
结论
这篇文章里我们写了若干种给 React 组件的类的方法绑定 this
值的方式。这些代码我已经在第二部分的基础上写了一些测试用例。
在下篇文章中,我们要讲的是用 ES2015 写 React 的时候,有关 state 的问题。