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