react的事件處理會丟失this,所以需要綁定,為什么會丟失this?
首先來看摘自官方的一句話:
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default.
這句話大概意思就是,你要小心jax回調函數里面的this,class方法默認是不會綁定它的
讓我十分疑惑,在我的知識范圍理解中,class是es6里面新增的方法,不就用來繼承原有對象上的屬性和方法創建新的對象嗎?就是代替原來的構造函數的一種更清晰的方式,為什么就不會綁定this呢?
可是查閱了一些es6的文檔,並不是這樣的啊,和class方法沒啥關系吧,為什么要它背鍋呢?
class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; // This binding is necessary to make `this` work in the callback this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> //這里調用的this也能拿到啊?? {this.state.isToggleOn ? 'ON' : 'OFF'} //這里的this為什么沒問題? </button> ); } }
這是官網上的一段代碼,如果是是因為class的關系,handleClick里面拿不到this,那為什么render里面能拿到this,所以和class根本沒關系吧本來就能拿到,那問題出現在哪里,為什么拿不到?
先看看解決辦法
第一種,在constructor里面用bind綁定this
constructor(props) { super(props); this.state = {isToggleOn: true}; // This binding is necessary to make `this` work in the callback this.handleClick = this.handleClick.bind(this); }
第二種,聲明方法的時候使用箭頭函數
handleClick = () => { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); }
第三種,調用的時候使用箭頭函數
render() { return ( <button onClick={ () => { this.handleClick } }> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); }
這個時候我想起了原生dom綁定click的方法
<button onclick ="handleClick()">點我</button>
兩者比較,我發現了個區別,原生的綁定方法事件名后面多了個()
於是我嘗試着在react里面的事件加一個()
render() { return ( <button onClick={ this.handleClick() }> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); }
就像上面這樣,然后我發現,無論我怎么點,都不會觸發這個方法了,再細心點,就發現,在渲染的時候,就調用了一次,而且僅此一次,再也不能調用了.
原因是jsx語法,渲染的時候會把{}里面包裹的代碼先解析一遍,因為如果加了括號,直接就執行了里面的函數,就沒有東西了,但是這個時候,this是可以拿到的
class App extends Component { handleClick(){ console.log(this); //下面調用加了(),這個時候發現,this是可以拿到的 } render() { return ( <div className="App"> <button onClick={this.handleClick()}>點我</button> //這里加了括號的 </div> ); } }
好像問題越來越明朗了,為啥會拿不到,和class沒有關系,完全是因為react自己封裝的東西,先會把{}里面的代碼解析一遍,於是大概就是下面這種情況了
const obj = { num:1 } obj.handleClick = function () { console.log(this); } console.log(eval(obj.handleClick )); // f(){ console.log(this) } react對{}的解析 (eval(obj.handleClick))() //onclick觸發點擊事件 這里輸出this是window,所以就等於丟失了this指向 console.log(eval(() => { obj.handleClick() })); // () => { obj.handleClick() } react對{}的解析 (eval(() => {obj.handleClick()}))() //onclick觸發點擊事件 這里輸出this還是obj,所以this就保留了