React中定義一個組件,能夠經過React.createClass或者ES6的class。本文討論的React組件是基於class定義的組件。採用class的方式,代碼結構更加清晰,可讀性強,並且React官方也推薦使用這種方式定義組件。前端
處理事件響應是Web應用中很是重要的一部分。React中,處理事件響應的方式有多種。react
先上代碼:git
//代碼1 class MyComponent extends React.Component { render() { return ( <button onClick={()=>{console.log('button clicked');}}> Click </button> ); } }
當事件響應邏輯比較複雜時,再把全部的邏輯直接寫在onClick的大括號內,就會致使render函數變得臃腫,不容易直觀地看出組件render出的元素結構。這時,能夠把邏輯封裝成組件的一個方法,而後在箭頭函數中調用這個方法。以下所示:github
//代碼2 class MyComponent extends React.Component { constructor(props) { super(props); this.state = {number: 0}; } handleClick() { this.setState({ number: ++this.state.number }); } render() { return ( <div> <div>{this.state.number}</div> <button onClick={()=>{this.handleClick();}}> Click </button> </div> ); } }
這種方式最大的問題是,每次render調用時,都會從新建立一個事件的回調函數,帶來額外的性能開銷,當組件的層級越低時,這種開銷就越大,由於任何一個上層組件的變化均可能會觸發這個組件的render方法。固然,在大多數狀況下,這點性能損失是能夠沒必要在乎的。這種方式也有一個好處,就是不須要考慮this的指向問題,由於這種寫法保證箭頭函數中的this指向的老是當前組件。babel
代碼先:app
//代碼3 class MyComponent extends React.Component { constructor(props) { super(props); this.state = {number: 0}; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState({ number: ++this.state.number }); } render() { return ( <div> <div>{this.state.number}</div> <button onClick={this.handleClick}> Click </button> </div> ); } }
這種方式的好處是每次render,不會從新建立一個回調函數,沒有額外的性能損失。須要注意的是,使用這種方式要在構造函數中爲事件回調函數綁定this: this.handleClick = this.handleClick.bind(this)
,不然handleClick
中的this是undefined。這是由於ES6 語法的緣故,ES6 的 Class 構造出來的對象上的方法默認不綁定到 this 上,須要咱們手動綁定。每次都手動綁定this是否是有點蛋疼?好吧,讓咱們來看下一種方式。函數
使用ES7的 property initializers,代碼能夠這樣寫:性能
//代碼4 class MyComponent extends React.Component { constructor(props) { super(props); this.state = {number: 0}; } handleClick = () => { this.setState({ number: ++this.state.number }); } render() { return ( <div> <div>{this.state.number}</div> <button onClick={this.handleClick}> Click </button> </div> ); } }
哈哈,不再用手動綁定this了。可是你須要知道,這個特性還處於試驗階段,默認是不支持的。若是你是使用官方腳手架Create React App 建立的應用,那麼這個特性是默認支持的。你也能夠自行在項目中引入babel的transform-class-properties插件獲取這個特性支持。this
事件的回調函數默認是會被傳入一個事件對象Event做爲參數的。若是我想傳入其餘參數給回調函數應該怎麼辦呢?spa
使用第一種方式的話很簡單,直接傳就能夠了:
//代碼5 class MyComponent extends React.Component { constructor(props) { super(props); this.state = { list: [1,2,3,4], current: 1 }; } handleClick(item,event) { this.setState({ current: item }); } render() { return ( <ul> {this.state.list.map( (item)=>( <li className={this.state.current === item ? 'current':''} onClick={(event) => this.handleClick(item, event)}>{item} </li> ) )} </ul> ); } }
使用第二種方式的話,能夠把綁定this的操做延遲到render中,在綁定this的同時,綁定額外的參數:
//代碼6 class MyComponent extends React.Component { constructor(props) { super(props); this.state = { list: [1,2,3,4], current: 1 }; } handleClick(item) { this.setState({ current: item }); } render() { return ( <ul> {this.state.list.map( (item)=>( <li className={this.state.current === item ? 'current':''} onClick={this.handleClick.bind(this, item)}>{item} </li> ) )} </ul> ); } }
使用第三種方式,解決方案和第二種基本一致:
//代碼7 class MyComponent extends React.Component { constructor(props) { super(props); this.state = { list: [1,2,3,4], current: 1 }; } handleClick = (item) => { this.setState({ current: item }); } render() { return ( <ul> {this.state.list.map( (item)=>( <li className={this.state.current === item ? 'current':''} onClick={this.handleClick.bind(undefined, item)}>{item} </li> ) )} </ul> ); } }
不過這種方式就有點雞肋了,由於雖然你不須要經過bind函數綁定this,但仍然要使用bind函數來綁定其餘參數。
關於事件響應的回調函數,還有一個地方須要注意。無論你在回調函數中有沒有顯式的聲明事件參數Event,React都會把事件Event做爲參數傳遞給回調函數,且參數Event的位置老是在其餘自定義參數的後面。例如,在代碼6和代碼7中,handleClick
的參數中雖然沒有聲明Event參數,但你依然能夠經過arguments[1]
獲取到事件Event對象。
總結一下,三種綁定事件回調的方式,第一種有額外的性能損失;第二種須要手動綁定this,代碼量增多;第三種用到了ES7的特性,目前並不是默認支持,須要Babel插件的支持,可是寫法最爲簡潔,也不須要手動綁定this。推薦使用第二種和第三種方式。
歡迎關注個人公衆號:老幹部的大前端,領取21本大前端精選書籍!