React 深入系列5:事件處理

文:徐超,《React進(jìn)階之路》作者

授權(quán)發(fā)布,轉(zhuǎn)載請注明作者及出處


React 深入系列5:事件處理

React 深入系列,深入講解了React中的重點(diǎn)概念、特性和模式等,旨在幫助大家加深對React的理解,以及在項(xiàng)目中更加靈活地使用React。

Web應(yīng)用中,事件處理是重要的一環(huán),事件處理將用戶的操作行為轉(zhuǎn)換為相應(yīng)的邏輯執(zhí)行或界面更新。在React中,處理事件響應(yīng)的方式有多種,本文將詳細(xì)介紹每一種處理方式的用法、使用場景和優(yōu)缺點(diǎn)。

使用匿名函數(shù)

先上代碼:

//代碼1
class MyComponent extends React.Component {
  render() {
    return (
      <button onClick={()=>{console.log('button clicked');}}>
        Click
      </button>
    );
  }
}

點(diǎn)擊Button的事件響應(yīng)函數(shù)是一個(gè)匿名函數(shù),這應(yīng)該是最常見的處理事件響應(yīng)的方式了。這種方式的好處是,簡單直接。哪里需要處理事件響應(yīng),就在哪里定義一個(gè)匿名函數(shù)處理。代碼1中的匿名函數(shù)使用的是箭頭函數(shù),我們也可以不使用箭頭函數(shù):

//代碼2
class MyComponent extends React.Component {
  render() {
    return (
      <button onClick={function(){console.log('button clicked');}}>
        Click
      </button>
    );
  }
}

雖然代碼2的運(yùn)行效果和代碼1相同,但實(shí)際項(xiàng)目中很少見到代碼2的這種寫法。這是因?yàn)榧^函數(shù)解決了this綁定的問題,可以將函數(shù)體內(nèi)的this綁定到當(dāng)前對象,而不是運(yùn)行時(shí)調(diào)用函數(shù)的對象。如果響應(yīng)函數(shù)中需要使用this.state,那么代碼2就無法正常運(yùn)行了。所以項(xiàng)目中一般直接使用箭頭函數(shù)定義的匿名函數(shù)作為事件響應(yīng)。

使用匿名函數(shù)的缺點(diǎn)是:當(dāng)事件響應(yīng)邏輯比較復(fù)雜時(shí),匿名函數(shù)的代碼量會很大,會導(dǎo)致render函數(shù)變得臃腫,不容易直觀地看出組件最終渲染出的元素結(jié)構(gòu)。另外,每次render方法調(diào)用時(shí),都會重新創(chuàng)建一個(gè)匿名函數(shù)對象,帶來額外的性能開銷,當(dāng)組件的層級越低時(shí),這種開銷就越大,因?yàn)槿魏我粋€(gè)上層組件的變化都可能會觸發(fā)這個(gè)組件的render方法。當(dāng)然,在大多數(shù)情況下,這點(diǎn)性能損失是可以不必在意的。

使用組件方法

代碼如下:

//代碼3
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {number: 0};
    this.handleClick = this.handleClick.bind(this); // 手動綁定this
  }

  handleClick() {
    this.setState({
      number: ++this.state.number
    });
  }
  
  render() {
    return (
      <div>
        <div>{this.state.number}</div>
        <button onClick={this.handleClick}>
          Click
        </button>
      </div>
    );
  }
}

點(diǎn)擊Button的事件響應(yīng)函數(shù)是組件的方法:handleClick。這種方式的好處是:每次render方法的調(diào)用,不會重新創(chuàng)建一個(gè)新的事件響應(yīng)函數(shù),沒有額外的性能損失。但是,使用這種方式要在構(gòu)造函數(shù)中為作為事件響應(yīng)的方法(handleClick),手動綁定this: this.handleClick = this.handleClick.bind(this),這是因?yàn)镋S6 語法的緣故,ES6 Class 的方法默認(rèn)不會把this綁定到當(dāng)前的實(shí)例對象上,需要我們手動綁定。每次都手動綁定this是不是有點(diǎn)繁瑣?好吧,讓我們來看下一種方式。

使用屬性初始化語法

使用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了。但是你需要知道,這個(gè)特性還處于試驗(yàn)階段,默認(rèn)是不支持的。如果你是使用官方腳手架Create React App 創(chuàng)建的應(yīng)用,那么這個(gè)特性是默認(rèn)支持的。你也可以自行在項(xiàng)目中引入babel的transform-class-properties插件獲取這個(gè)特性支持。

事件響應(yīng)函數(shù)的傳參問題

事件響應(yīng)函數(shù)默認(rèn)是會被傳入一個(gè)事件對象Event作為參數(shù)的。如果想傳入其他參數(shù)給響應(yīng)函數(shù)應(yīng)該怎么辦呢?

使用第一種方式的話很簡單,直接使用新參數(shù):

//代碼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>
    );
  }
}

onClick的響應(yīng)函數(shù)中,方法體內(nèi)可以直接使用新的參數(shù)item。

使用第二種方式的話,可以把綁定this的操作延遲到render中,在綁定this的同時(shí),綁定額外的參數(shù):

//代碼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>
    );
  }
}

不過這種方式就有點(diǎn)雞肋了,因?yàn)殡m然你不需要通過bind函數(shù)綁定this,但仍然要使用bind函數(shù)來綁定其他參數(shù)。

關(guān)于事件響應(yīng)函數(shù),還有一個(gè)地方需要注意。不管你在響應(yīng)函數(shù)中有沒有顯式的聲明事件參數(shù)Event,React都會把事件Event作為參數(shù)傳遞給響應(yīng)函數(shù),且參數(shù)Event的位置總是在其他自定義參數(shù)的后面。例如,在代碼6和代碼7中,handleClick的參數(shù)中雖然沒有聲明Event參數(shù),但你依然可以通過arguments[1]獲取到事件Event對象。

總結(jié)一下,三種事件處理的方式,第一種有額外的性能損失;第二種需要手動綁定this,代碼量增多;第三種用到了ES7的特性,目前并非默認(rèn)支持,需要Babel插件的支持,但是寫法最為簡潔,也不需要手動綁定this。一般推薦使用第二種和第三種方式。


新書推薦《React進(jìn)階之路》

作者:徐超

畢業(yè)于浙江大學(xué),碩士,資深前端工程師,長期就職于能源物聯(lián)網(wǎng)公司遠(yuǎn)景智能。8年軟件開發(fā)經(jīng)驗(yàn),熟悉大前端技術(shù),擁有豐富的Web前端和移動端開發(fā)經(jīng)驗(yàn),尤其對React技術(shù)棧和移動Hybrid開發(fā)技術(shù)有深入的理解和實(shí)踐經(jīng)驗(yàn)。

image

image

美團(tuán)點(diǎn)評廣告平臺大前端團(tuán)隊(duì)招收2019\2020年前端實(shí)習(xí)生(偏動效方向)

有意者郵件:yao.zhou@meituan.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,535評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,917評論 25 709
  • kmokidd ·3 天前 本篇為聯(lián)合翻譯,譯者:寸志,范洪春,kmokidd,姜天意 數(shù)月前,F(xiàn)acebook ...
    東引甌越閱讀 2,036評論 0 49
  • 小黃來自深圳,他的愛人和我是同鄉(xiāng),今年兩口子一同回來過年。 有朋自遠(yuǎn)方來,不亦樂乎。我要請小黃兩口子吃飯。 有緣千...
    我在創(chuàng)業(yè)閱讀 388評論 0 1
  • 感恩網(wǎng)絡(luò)的便利,可以聆聽到姚老師關(guān)于財(cái)富增長的三輪體空,可以看到各地在財(cái)富、關(guān)系、創(chuàng)意等方面的種子達(dá)人的分享~感恩...
    騫卉閱讀 251評論 0 1

友情鏈接更多精彩內(nèi)容