react提示setState is not defined,但是bind了this缺解决了。为什么?

constructor(props){
    super(props);
    this.state = {liked:false}
    //this.handleClick = this.handleClick.bind(this);
}
handleClick() {
    this.setState({
        liked:!this.state.liked
    });
}
render里的代码:
...
let text = this.state.liked ? 'like' : 'haven\'t liked';
...
<p onClick={this.handleClick}>you{text}</p>
...

如果这句不加代码中的注释部分,则报这个错误:setState is not defined
如果是es5,则不需要加这个,因为看各教程中并没有写这句,故请教下高玩。。。
阅读 7.8k
3 个回答

先讲结论:

这是因为ES6写法,也就是类(Class)的写法,React默认不会自动绑定类中的方法造成的。
React对ES5的语法是默认有自动绑定,所以不需要加这个。
而且,自动绑不绑定也是React决定要不要的

后说缘由:

React的元件从定义开始,到执行完成渲染到真实的DOM元素中,是一个很复杂的过程,这里面也包含了事件处理函式到底是要用什么方式来执行。事件处理函式原本就是一个很特别的回调函式,当在事件触发时是要执行从什么地方来的函式,是在元件自己本身定义的函式(方法)?还是上层元件定义的函式(方法)?或者来自外部的模组或函式?执行的对象又是谁?这些问题都要一起考量。

要讲到函式的this值与上下文(Context),先需要对JavaScript再科普一下,摘要出来在下面说明。在JavaScript中,函式一共有以下四种呼叫的样式,它们分别是:

  • 一般的函式呼叫(Function Invocation Pattern)

  • 物件中的方法呼叫(Method Invocation Pattern)

  • 建构函式呼叫(Constructor Invocation Pattern)

  • 使用apply, call, bind方法呼叫(应用呼叫样式 Apply invocation pattern / 间接呼叫样式 Indirect Invocation Pattern)

对函式库或框架(或是对具熟练技术的开发者)来说,最具弹性的是最后一种,因为它可以提供最多的方便性,以间接的呼叫样式,在必要的时候才进行呼叫,也可以转换函式(方法的)上下文(Context)。当你真正理解为何this值的真正涵义时,你会发现以下几个事实,这可能会颠覆对于JavaScript中函式的原先想像:

"函式定义是定义,呼叫是呼叫": 实际上在物件定义的所谓方法,你可以把它当作只是让开发者方便集中管理的函式定义。

  • this值来说,它根本不关心函式是在哪里定义或是怎么定义的,它只关心是谁呼叫了它。

  • 所有的函式在呼叫时,其实都有一个拥有者物件来进行呼叫"。所以你可以说,其实所有函式都是物件中的"方法。所有的函式执行都是以Object.method()方式呼叫。

回到React中,新版本(0.13后)允许我们可以使用ES6的类(Class)定义方式来定义组件,这称之为"建构函式呼叫样式",但原本的工厂样式(也就是使用React.createClass方法,或称为ES5语法方式)并没有因此而废止不用,也就是说这两种定义方式在基本使用上是可以得到相等结果的,只是里面所使用的语法细节有所不同。对于React来说,实际上它根本也不允许你在使用new来产生ES6+类定义的实体,那它的内部又是如何来呼叫或执行这些我们在类里面定义的方法的?

答案已经很明显了,绝对不是单纯的"建构函式呼叫样式",它在程式码中混用了多种的函式呼叫样式,而且会使用最具弹性的那种"间接的呼叫样式",也就是说它明显的使用了this中"函式定义是定义,呼叫是呼叫"的作法。

在React官方对建构式样式(ES6类别)定义方式与工厂样式(也就是使用React.createClass方法)的说明中,有一个非常大的差异,称为"Autobinding"(自动绑定),工厂样式中的this值是会自动绑定的,但建构式样式(ES6类别)定义方式的this值"不会"再自动绑定。

自动绑不绑定最大的影响会在于当元件的render方法中,在React元素(JSX语法)中呼叫我们定义的函式(方法)时,最早之前版本的工厂样式也不会自动绑定,是后来加入的功能(v0.4, 2013),出自[React官网这里] (https://facebook.github.io/re...

由此得知,自动绑定并非一个JavaScript中的自然内部机制,而是由React自己所决定的的内部实作功能。但在v0.13(2015)版本中取消了ES6类别里面这个机制,理由除了像上面所说的,当在事件触发时要执行的回调函式,来源可能有很多种,对象或许并非单一。另一个主要的理由是为了不让开发者造成混乱,以为在ES6类别里这个机制是自然就有的,所以在建构式样式(ES6类别)后改成了你需要手动绑定。

要绑定类别中的方法(函式)可用下面两种语法,出自React官网这里:

第一种是在建构式中constructor加入绑定你定义的方法的语句:

class Counter extends React.Component {
  constructor() {
    super()
    this.tick = this.tick.bind(this);
  }
  tick() {
    //...
  }
  //...
}

第二种是用箭头函式语法来宣告方法:

class Counter extends React.Component {
  tick = () => {
    //...
  }
  //...
}

要注意的这个语法既非ES6也非ES7,暂且称它为ES7+或ES8,但babel工具可以正确的转译这个语法,所以你不需要担心会有执行上的问题。第二种需要加装babel-plugin-transform-class-properties,并在.babelrc中配置到plugins中,参考https://babeljs.io/docs/plugi...

最后的结论是,JSX是React创的,babel是React养的,自动绑不绑定也是React决定要不要的。

<p onClick={()=>this.handleClick()}>you{text}</p>

我认为原因是es5的实现中因为标准function 都是不bind的,所以reactjs帮用户绑定了,省的麻烦。而es6中有了()=> 这个写法,这个写法会自动bind的,所以对于es6的编译就不需要reactjs再帮用户绑定了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏