[譯] React 組件中綁定回調

原文:Binding callbacks in React componentsreact

在組件中給事件綁定處理函數是很常見的,好比說每當用戶點擊一個button的時候使用console.log打印一些東西。函數

class DankButton extends React.Component {
  render() {
    return <button onClick={this.handleClick}>Click me!</button>
  }
  
  handleClick() {
    console.log(`such knowledge`)
  }
}

很好,這段代碼會知足你的需求,那如今若是我想在handleClick()內調用另一個方法,好比logPhrase()性能

class DankButton extends React.Component {
  render() {
    return <button onClick={this.handleClick}>Click me!</button>
  }
  
  handleClick() {
    this.logPhrase()
  }
  
  logPhrase() {
    console.log('such gnawledge')
  }
}

這樣居然不行,會獲得以下的錯誤提醒this

TypeError: this.logPhrase is not a function at handleClick (file.js:36:12)

當咱們把handleClick綁定到 onClick的時候咱們傳遞的是一個函數的引用,真正調用handleClick的是事件處理系統。所以handleClickthis上下文和我門想象的this.logPhrase()是不同的。code

這裏有一些方法可讓this指向DankButton組件。component

很差的方案 1:箭頭函數

箭頭函數是在ES6中引入的,是一個寫匿名函數比較簡潔的方式,它不單單是包裝匿名函數的語法糖,箭頭函數沒有本身的上下問,它會使用被定義的時候的this做爲上下文,咱們能夠利用這個特性,給onClick綁定一個箭頭函數。事件

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')
  }
}

然而,我並不推薦這種解決方式,由於箭頭函數定義在render內部,組件每次從新渲染都會建立一個新的箭頭函數,在React中渲染是很快捷的,因此從新渲染會常常發生,這就意味着前面渲染中產生的函數會堆在內存中,強制垃圾回收機制清空它們,這是很花費性能的。內存

很差的方案 2:this.handleClick.bind(this)

另一個解決這個問題的方案是,把回調綁定到正確的上下問thisget

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')
  }
}

這個方案和箭頭函數有一樣的問題,在每次render的時候都會建立一個新的函數,可是爲何沒有使用匿名函數也會這樣呢,下面就是答案。it

function test() {}

const testCopy = test
const boundTest = test.bind(this)

console.log(testCopy === test) // true
console.log(boundTest === test) // false

.bind並不修改原有函數,它只會返回一個指定執行上下文的新函數(boundTest和test並不相等),所以垃圾回收系統仍然須要回收你以前綁定的回調。

好的方案:在構造函數(constructor)中bind handleClick

仍然使用 .bind ,如今咱們只要繞過每次渲染都要生成新的函數的問題就能夠了。咱們能夠經過只在構造函數中綁定回調的上下問來解決這個問題,由於構造函數只會調用一次,而不是每次渲染都調用。這意味着咱們沒有生成一堆函數而後讓垃圾回收系統清除它們。

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')
  }
}

很好,如今咱們的函數被綁定到正確的上下文,並且不會在每次渲染的時候建立新的函數。

若是你使用的是React.createClass而不是ES6的classes,你就不會碰到這個問題,createClass生成的組件會把它們的方法自動綁定到組件的this,甚至是你傳遞給事件回調的函數。

相關文章
相關標籤/搜索