原文:Binding callbacks in React components
在組件中給事件綁定處理函數(shù)是很常見的雪情,比如說每當(dāng)用戶點(diǎn)擊一個button的時候使用console.log
打印一些東西椭住。
class DankButton extends React.Component {
render() {
return <button onClick={this.handleClick}>Click me!</button>
}
handleClick() {
console.log(`such knowledge`)
}
}
很好,這段代碼會滿足你的需求旋廷,那現(xiàn)在如果我想在handleClick()
內(nèi)調(diào)用另外一個方法,比如logPhrase()
class DankButton extends React.Component {
render() {
return <button onClick={this.handleClick}>Click me!</button>
}
handleClick() {
this.logPhrase()
}
logPhrase() {
console.log('such gnawledge')
}
}
這樣竟然不行礼搁,會得到如下的錯誤提醒
TypeError: this.logPhrase is not a function at handleClick (file.js:36:12)
當(dāng)我們把handleClick
綁定到 onClick
的時候我們傳遞的是一個函數(shù)的引用饶碘,真正調(diào)用handleClick
的是事件處理系統(tǒng)。因此handleClick
的this
上下文和我門想象的this.logPhrase()
是不一樣的馒吴。
這里有一些方法可以讓this
指向DankButton組件扎运。
不好的方案1:箭頭函數(shù)
箭頭函數(shù)是在ES6中引入的,是一個寫匿名函數(shù)比較簡潔的方式饮戳,它不僅僅是包裝匿名函數(shù)的語法糖豪治,箭頭函數(shù)沒有自己的上下問调衰,它會使用被定義的時候的this
作為上下文喜喂,我們可以利用這個特性宦芦,給onClick
綁定一個箭頭函數(shù)蒙揣。
class DankButton extends React.Component {
render() {
// Bad Solution: An arrow function!
return <button onClick={() => this.handleClick()}>Click me!</button>
}
handleClick() {
this.logPhrase()
}
logPhrase() {
console.log('such gnawledge')
}
}
然而渐排,我并不推薦這種解決方式,因為箭頭函數(shù)定義在render
內(nèi)部戈轿,組件每次重新渲染都會創(chuàng)建一個新的箭頭函數(shù)玻侥,在React中渲染是很快捷的,所以重新渲染會經(jīng)常發(fā)生涣脚,這就意味著前面渲染中產(chǎn)生的函數(shù)會堆在內(nèi)存中示辈,強(qiáng)制垃圾回收機(jī)制清空它們,這是很花費(fèi)性能的遣蚀。
不好的方案2:this.handleClick.bind(this)
另外一個解決這個問題的方案是矾麻,把回調(diào)綁定到正確的上下問this
class DankButton extends React.Component {
render() {
// Bad Solution: Bind that callback!
return <button onClick={this.handleClick.bind(this)}>Click me!</button>
}
handleClick() {
this.logPhrase()
}
logPhrase() {
console.log('such gnawledge')
}
}
這個方案和箭頭函數(shù)有同樣的問題,在每次render
的時候都會創(chuàng)建一個新的函數(shù)芭梯,但是為什么沒有使用匿名函數(shù)也會這樣呢险耀,下面就是答案。
function test() {}
const testCopy = test
const boundTest = test.bind(this)
console.log(testCopy === test) // true
console.log(boundTest === test) // false
.bind
并不修改原有函數(shù)玖喘,它只會返回一個指定執(zhí)行上下文的新函數(shù)(boundTest和test并不相等)甩牺,因此垃圾回收系統(tǒng)仍然需要回收你之前綁定的回調(diào)。
好的方案:在構(gòu)造函數(shù)(constructor)中bind handleClick
仍然使用 .bind
累奈,現(xiàn)在我們只要繞過每次渲染都要生成新的函數(shù)的問題就可以了贬派。我們可以通過只在構(gòu)造函數(shù)中綁定回調(diào)的上下問來解決這個問題,因為構(gòu)造函數(shù)只會調(diào)用一次澎媒,而不是每次渲染都調(diào)用搞乏。這意味著我們沒有生成一堆函數(shù)然后讓垃圾回收系統(tǒng)清除它們。
class DankButton extends React.Component {
constructor() {
super()
// Good Solution: Bind it in here!
this.handleClick = this.handleClick.bind(this)
}
render() {
return <button onClick={this.handleClick}>Click me!</button>
}
handleClick() {
this.logPhrase()
}
logPhrase() {
console.log('such gnawledge')
}
}
很好戒努,現(xiàn)在我們的函數(shù)被綁定到正確的上下文请敦,而且不會在每次渲染的時候創(chuàng)建新的函數(shù)。
如果你使用的是React.createClass
而不是ES6的classes储玫,你就不會碰到這個問題冬三,createClass
生成的組件會把它們的方法自動綁定到組件的this
,甚至是你傳遞給事件回調(diào)的函數(shù)缘缚。