你需要了解的 react 事件處理和繫結方法
import React, { Component } from 'react'; import { render } from 'react-dom'; class LikeButton extends Component { constructor(props) { super(props); this.state = { liked: false }; } handleClick(e) { this.setState({ liked: !this.state.liked }); } render() { const text = this.state.liked ? 'like' : 'haven\'t liked'; return ( <p onClick={this.handleClick.bind(this)}> You {text} this. Click to toggle. </p> ); } } render( <LikeButton />, document.getElementById('example') ); 複製程式碼
可以看到 React 裡面繫結事件的方式和在 HTML 中繫結事件類似,使用駝峰式命名指定要繫結的 onClick 屬性為元件定義的一個方法 {this.handleClick.bind(this)}。
注意要顯式呼叫 bind(this) 將事件函式上下文繫結要元件例項上,這也是 React 推崇的原則:沒有黑科技,儘量使用顯式的容易理解的 JavaScript 程式碼。
2、'合成事件'和 '原生事件'
-
React 實現了一個“合成事件”層(synthetic event system),這個事件模型保證了和 W3C 標準保持一致,所以不用擔心有什麼詭異的用法,並且這個事件層消除了 IE 與 W3C 標準實現之間的相容問題。
- “合成事件”還提供了額外的好處:
2.1 事件委託
- 事件委託就是利用事件冒泡原理,把處理任務委託給父元素或者祖先元素(通常用父元素),我們通過目標物件來判斷事件源,並執行事件處理。
-
“合成事件”會以事件委託(event delegation)的方式繫結到元件最上層,並且在元件解除安裝(unmount)的時候自動銷燬繫結的事件。
2.2 原生事件
比如你在 componentDidMount 方法裡面通過 addEventListener 繫結的事件就是瀏覽器原生事件。
使用原生事件的時候注意在 componentWillUnmount 解除繫結 removeEventListener。
Waring:如果不在元件銷燬的時候解除事件的話,會造成記憶體洩露的問題。
怎麼解決這個問題?這裡可以看我的相關文章 react 記憶體洩露常見問題解決方案
所有通過 JSX 這種方式繫結的事件都是繫結到“合成事件”,除非你有特別的理由,建議總是用 React 的方式處理事件。
3、事件繫結的幾種方法
- 由於類的方法預設不會繫結this,因此在呼叫的時候如果忘記繫結,this的值將會是undefined。 通常如果不是直接呼叫,應該為方法繫結this。繫結方式有以下幾種:
3.1、 在建構函式中使用bind繫結this
class Button extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick(){ console.log('this is:', this); } render() { return ( <button onClick={this.handleClick}> Click me </button> ); } } 複製程式碼
3.2、在呼叫的時候使用 bind 去繫結 this
class Button extends React.Component { handleClick(){ console.log('this is:', this); } render() { return ( <button onClick={this.handleClick.bind(this)}> Click me </button> ); } } 複製程式碼
3.3、呼叫的時候使用箭頭函式繫結 this
class Button extends React.Component { handleClick(){ console.log('this is:', this); } render() { return ( <button onClick={()=>this.handleClick()}> Click me </button> ); } } 複製程式碼
3.4、使用屬性初始化語法直接繫結
class Button extends React.Component { handleClick=()=>{ console.log('this is:', this); } render() { return ( <button onClick={this.handleClick}> Click me </button> ); } } 複製程式碼
3.5、比較上訴幾個方式的優劣
- 3.2和 3.3 方法都是呼叫的時候再繫結 this
- 優點 : 寫法簡單,元件中沒有 state 的時候不需要新增建構函式來繫結 this
- 缺點 : 每一次呼叫的時候都會生成一個新的方法例項,因此對效能有影響,並且當這個函式作為屬性值傳入低階元件的時候,這些元件可能會進行額外的重新渲染,因為每一次都是新的方法例項作為的新的屬性傳遞。
- 3.1 方法在建構函式中綁定了 this,呼叫的時候不需要二次繫結
- 優點 :只會生成一個方法例項,並且繫結一次之後如果多次用到這個方法也不需要綁定了。
- 缺點 :即使不適用 state 的時候也需要在建構函式中繫結 this,程式碼量多。
- 3.4 方法 利用屬性初始化語法,將方法初始化為箭頭函式,因此在建立函式的時候就綁定了this。
- 優點 :建立方法就繫結this,不需要在類建構函式中繫結,呼叫的時候不需要再作繫結。結合了方式1、方式2、方式3的優點
- 缺點 :以前有需要 babel 轉移的需求,現在隨著效能越來越好,也會考慮這一點消耗的問。
3.6 怎麼傳參?
- 給事件處理函式傳遞額外引數的方式:bind(this, arg1, arg2, ...)
- 非 bind 的直接傳參就可以了。
render: function() { return <p onClick={this.handleClick.bind(this, 'extra param')}>; }, handleClick: function(param, event) { // handle click } 複製程式碼