你須要瞭解的 react 事件處理和綁定方法

一、事件處理

先看一個 demo

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)}。javascript

注意要顯式調用 bind(this) 將事件函數上下文綁定要組件實例上,這也是 React 推崇的原則:沒有黑科技,儘可能使用顯式的容易理解的 JavaScript 代碼。html

二、'合成事件'和 '原生事件'

  • React 實現了一個「合成事件」層(synthetic event system),這個事件模型保證了和 W3C 標準保持一致,因此不用擔憂有什麼詭異的用法,而且這個事件層消除了 IE 與 W3C 標準實現之間的兼容問題。java

  • 「合成事件」還提供了額外的好處:react

2.1 事件委託

  • 事件委託就是利用事件冒泡原理,把處理任務委託給父元素或者祖先元素(一般用父元素),咱們經過目標對象來判斷事件源,並執行事件處理。
  • image

「合成事件」會以事件委託(event delegation)的方式綁定到組件最上層,而且在組件卸載(unmount)的時候自動銷燬綁定的事件。瀏覽器

2.2 原生事件

  • 好比你在 componentDidMount 方法裏面經過 addEventListener 綁定的事件就是瀏覽器原生事件。babel

  • 使用原生事件的時候注意在 componentWillUnmount 解除綁定 removeEventListener。dom

Warning: 若是不在組件銷燬的時候解除事件的話,會形成內存泄露的問題。 函數

怎麼解決這個問題?這裏能夠看個人相關文章 react 內存泄露常見問題解決方案post

全部經過 JSX 這種方式綁定的事件都是綁定到「合成事件」,除非你有特別的理由,建議老是用 React 的方式處理事件。性能

三、事件綁定的幾種方法

  • 因爲類的方法默認不會綁定this,所以在調用的時候若是忘記綁定,this的值將會是undefined。 一般若是不是直接調用,應該爲方法綁定this。綁定方式有如下幾種:

3.一、 在構造函數中使用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.二、在調用的時候使用 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.三、調用的時候使用箭頭函數綁定 this

class Button extends React.Component {
  handleClick(){
    console.log('this is:', this);
  }
  render() {
    return (
      <button onClick={()=>this.handleClick()}> Click me </button>
    );
  }
}
複製代碼

3.四、使用屬性初始化語法直接綁定

class Button extends React.Component {
  handleClick=()=>{
    console.log('this is:', this);
  }
  render() {
    return (
      <button onClick={this.handleClick}> Click me </button>
    );
  }
}
複製代碼

3.五、比較上訴幾個方式的優劣

  • 3.2和 3.3 方法都是調用的時候再綁定 this
    • 優勢: 寫法簡單,組件中沒有 state 的時候不須要添加構造函數來綁定 this
    • 缺點: 每一次調用的時候都會生成一個新的方法實例,所以對性能有影響,而且當這個函數做爲屬性值傳入低階組件的時候,這些組件可能會進行額外的從新渲染,由於每一次都是新的方法實例做爲的新的屬性傳遞。
  • 3.1 方法在構造函數中綁定了 this,調用的時候不須要二次綁定
    • 優勢:只會生成一個方法實例,而且綁定一次以後若是屢次用到這個方法也不須要綁定了。
    • 缺點:即便不適用 state 的時候也須要在構造函數中綁定 this,代碼量多。
  • 3.4 方法 利用屬性初始化語法,將方法初始化爲箭頭函數,所以在建立函數的時候就綁定了this。
    • 優勢:建立方法就綁定this,不須要在類構造函數中綁定,調用的時候不須要再做綁定。結合了方式一、方式二、方式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 } 複製代碼

3.7 事件綁定方法總結

方式1是官方推薦的綁定方式,也是性能最好的方式。方式2和方式3會有性能影響而且當方法做爲屬性傳遞給子組件的時候會引發重渲問題。方式4是性能比較好的,如今 babel 已經很成熟了,推薦你們使用

參考

相關文章
相關標籤/搜索