React 中事件處理

2023-11-28 21:03:36

不要問自己需要什麼樣的人生,而要問自己想要成為什麼樣的人。

我們從前面的學習知道一個 React 元件不僅僅只包含 DOM 結構的,還應該樣式和 Javascript 邏輯的。這裡我們認識邏輯構造之事件處理。

1. React 事件處理

這裡列舉了在 React 中事件的幾種繫結處理方式:

import React, { Component } from "react";

class App extends Component {
  render () {
    return (
      <div>
        <input/>
        <button onClick={ ()=>{ console.log("第一種事件繫結處理方式") }}>Add-1</button>
        <button onClick={ this.handleClick2 }>Add-2</button>
        <button onClick={ this.handleClick3 }>Add-3</button>
        <button onClick={ ()=>{ this.handleClick4() } }>Add-4</button>
      </div>
    )
  }

  handleClick2() {
    console.log("第二種事件繫結處理方式")
  }

  handleClick3 = ()=>{
    console.log("第三種事件繫結處理方式")
  }

  handleClick4 = ()=>{
    console.log("第四種事件繫結處理方式")
  }
}

export default App;

2. 事件繫結區別

這裡重點說明下在事件和事件繫結繫結中 this 指向問題。

2.1 匿名函數

直接執行匿名函數,直接在 {} 中寫事件函數表示式。

寫法特點:

  • 適合邏輯少、簡單表示式。如果處理邏輯過多、複雜 ,會導致結構不清晰,難維護,不推薦。
  • 事件內部 this 指向和外部一致,因為箭頭函數沒有 this 指向問題原則。

能直接存取:

  • 因為 onClick 後面表示式跟的是一個函數(箭頭函數),這裡事件內部 this 指向和外部一致。
class App extends Component {
 // 定義屬性
 value = 100

 render () {
  return (
    <div>
      <input/>
      <button onClick={ ()=>{ console.log("第一種事件繫結處理方式", this.value) }}>Add-1</button>
    </div>
  )
}

2.2 呼叫內部普通函數

寫法特點:this 指向和外部不一致,需要用 bind 修正 this 指向,不推薦使用。

<button onClick={ this.handleClick2 }>Add-2</button>
// 修正後:
<button onClick={ this.handleClick2.bind(this) }>Add-2</button>

handleClick2() {
  // 異常,需要通過改變 this 指向解決
  console.log("第二種事件繫結處理方式", this.value) 
}

不能直接存取:

這裡存取類屬性 this.value 會報錯,我們可以列印出 this 看下它指向什麼,結果會是:undefined。為什麼 this 會丟失呢?記住一句話:函數中的 this 誰呼叫我,this 就指向誰。這裡點完按鈕後被 React 事件系統呼叫的,this 指向的應該是 React 事件系統。用於不會指向 App 這個範例。而這裡它也沒有指向 React 事件系統,而是丟了指向 undefined。哈哈哈....

2.3 呼叫內部箭頭函數

寫法特點:this 指向和外部一直,沒有 this 指向問題,推薦使用。

<button onClick={ this.handleClick3 }>Add-3</button>

handleClick3 = ()=>{
  console.log("第三種事件繫結處理方式", this.value)
}

這裡是箭頭函數,this 指向根本不關心誰呼叫的我,它永遠保持與外部作用域一樣的,它指向的 app 的範例。為什麼箭頭函數 this 指向就不關心誰呼叫的我呢?我也不知道.... 難到....

  • 箭頭函數會自動改變 this 的指向???
  • 或者箭頭函數不是改變 this 指向,而是參照上一個作用域的 this ???
  • 一個比較權威的解釋是在箭頭函數中,this 與封閉詞法上下文的 this 保持一致。在全域性程式碼中,它將被設定為全域性物件。

2.4 執行匿名函數,呼叫其他內部函數

寫法特點:this 指向和外部一直,沒有 this 指向問題,符合誰呼叫我我指向誰。非常推薦使用這種寫法,引數傳遞很方便。

<button onClick={ ()=>{ this.handleClick4() } }>Add-4</button>
// 語法簡寫:
<button onClick={ ()=>this.handleClick4() }>Add-4</button>

// 有人說這裡是因為你寫成了箭頭函數了吧,即使他不寫成箭頭函數也沒關係,剛才講的原理,符合誰呼叫我我指向誰。
handleClick4 = ()=>{
  console.log("第四種事件繫結處理方式", this.value)
}

整體有個問題: 要不要加小括號,不加不讓他自己主動執行,點選系統會呼叫、加小括號執行函數。加小闊號主動執行,點選後不執行 undefined。

2.5 JS 中修正 this 指向方案

  • call:改變 this 指向,自動執行函數;
  • apply:改變 this 指向,自動執行函數;
  • bind:改變 this 指向,不會自動執行函數,需要手動加括號執行函數 ;
var obj1 = {
  name: "obj1",
  getName() {
    console.log(this.name)
  }
}

var obj2 = {
  name: "obj2",
  getName() {
    console.log(this.name)
  }
}

// this.name 誰呼叫我我指向誰
obj1.getName() // 結果 obj1
obj2.getName() // 結果 obj2

// call, reply :修改 obj1 getName 中 this 指向 obj2
obj1.getName.call(obj2) // 結果 obj2
obj2.getName() // 結果 obj2

3. 總結事件處理

3.1 this 指向問題,記住兩句話

  • 誰呼叫我我指向誰原則;
  • 箭頭函數沒有 this 指向問題;

3.2 React 事件繫結和原生事件繫結有什麼區別?

React 中事件繫結沒有繫結到具體的 DOM 節點(元素)身上。它採用的是一種事件代理的模式,繫結在根節點身上。繫結到每一個 DOM 節點身上是很消耗記憶體的。

React 模擬了一套事件冒泡機制,等冒泡到根節點上通過 target 事件源找到是那個元素真實觸發的,然後從這個觸發的元素到頂點所有節點都去查一查,有沒有一個叫 onClick 屬性,如果有就把這個 onClick 事件給執行了,完整模擬冒泡的流程,即模擬系統事件機制。其不需要考慮解綁、移除事件等,只有節點從頁面中沒了,onClick 根本就不會再有了,沒有繫結只有節點沒了 onClick 就沒了。

3.3 Event 事件物件也是支援的

Event 物件,和普通瀏覽器一樣,事件 handler 會被自動傳入一個 event 物件,這個物件和普通的瀏覽器 event 物件所包含的方法和屬性都基本一致。不同的是 React 中的 event 物件並不是瀏覽器提供的,而是它自己內部所構建的。它同樣具有 event . stoppropagation、event.preventDefault 這種常用的方法。