react报错 TypeError: Cannot read property 'setState' of undefined

代码以下:html

class test extends Component {
    constructor(props) {
        super(props);
        this.state = {
            liked: false
        };
    }
    handleClick(event) {
        this.setState({liked: !this.state.liked});
    }
    render() {
        var text = this.state.liked ? '喜欢' : '不喜欢';
        return (
            <div onClick={this.handleClick}>
                你<b>{text}</b>我。点我切换状态。
            </div>
        );
    }
 
}
export default test;

能够正常展现页面:
在这里插入图片描述
可是按钮点击就会报错。
在这里插入图片描述
为何会出现这种状况?
由于点击按钮是,到了handleClick() 方法中的this 已经不是组件里的this了。
第一种解决方法是: 手动绑定this。将react

constructor(props) {
    super(props);
    this.state = {
        liked: false
    };
}

改成git

constructor(props) {
    super(props);
    this.state = {
        liked: false
    };
    this.handleClick = this.handleClick.bind(this);//手动绑定
}

第二种解决方法是: 将github

handleClick(event) {
        this.setState({liked: !this.state.liked});
}

改成web

handleClick= (e) => {
        this.setState({liked: !this.state.liked});
}

这种解决方法之因此能解决问题,就是引伸到了另一个问题: 函数做为react 组件的方法时,箭头函数和普通函数的却别是什么?
举个栗子: 下面2个a 的区别定义有什么区别?babel

class App extends Component {
  a() {
    console.log(1)
  }
 
  a = () => {
    console.log(1)
  }
}

第一个a没必要说,是原型方法的定义,宽松模式下对应ES5 就是svg

App.prototype.a = function() {}

第二个是 Stage 2 Public Class Fields 里面的写法,babel 下须要用 Class properties transform Plugin 进行转义。至关于:函数

class App extends Component {
  constructor (...args) {
    super(...args)
    this.a = () => {
        console.log(1)
    }
  }
}

为何须要第二种写法?
在react 里边,要讲类的原型方法经过props 传给子组件,传统写法必需要bind(this),不然方法执行时this会找不到:优化

<button onClick={this.handleClick.bind(this)}></button>

或者this

<button onClick={(e) => this.handleClick(e)}></button>

这种写法难看不说,还会对react组件的 shouldComponentUpdate 优化形成影响。
这是由于react提供了shouldComponentUpdate 让开发者可以控制避免没必要要的render,还提供了再shouldComponentUpdate自动进行 Shallow Compare 的react.PUreComponent 继承自 PureComponent 的组件,只要props和state 中的值不变,组件就不会从新render。

然而若是用了bind this,每次父组件渲染,传给子组件的props.onClick 都会变,PureComponent 的 Shallow Compare 基本上就是小了,除非你手动实现
shouldComponentUpdate

使用 Public Class Fields 的这种写法,就解决了这个问题。灵位还有其余若干种办法,好比先定义原型方法,而后在constructor 里边bind 一遍,或者使用decorator 进行bind 等:

class A {
  constructor() {
    this.a = this.a.bind(this)
  }

  a() {}

  // or
  @bindthis
  b() {}
}

而箭头函数除了代码少,与普通函数最大的不一样就是: this 是由声明该函数时候定义的,通常是隐性定义为声明该函数时的做用域this。

var a = ()=>{
    console.log(this)
}
//等同于
var a = function(){
    console.log(this)
}.bind(this);
 
a(); //Window
var b = function(){
    console.log(this)
};
b(); //Window
var obj = { a,b };
obj.a(); //Window
obj.b(); //obj

箭头函数最大的做用是使得this 从正常状况下的动态做用域(根据运行位置肯定值)变成了静态做用域(根据定义位置肯定值,也就是词法做用域)。

若想了解得更详细,能够去阅读官方文档:https://reactjs.org/docs/handling-events.html