iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 13
1
Modern Web

I Want To Know React系列 第 13

I Want To Know React - 處理 Event

如 HTML 一樣,JSX 也提供了處理 DOM event 的方法。就讓我們來了解一下語法吧!

JSX 處理 event 語法

JSX 註冊 event 語法與 HTML 註冊 event 的語法十分相似,只要把 function 加入 DOM element attribute 的 value 中即可,但因為 JSX 與 HTML 特性不同,因此語法上還是有些區別。

註冊 event 語法為 JSX 基本語法的應用

JSX 註冊 event 的語法只是 JSX 基礎語法的應用而已。

就像初探 JSXJSX 語法章節提到的,JSX 底層即為 JavaScript,因此 JSX 的語法特性比起 HTML 還是會更接近 JavaScript。只要熟悉這個觀念,並了解 JSX 的 expression 與 attribute 命名方式的話,就可以輕鬆寫出註冊 event 的語法。

JSX 與 HTML 在註冊 event 的語法上會有以下這幾項差異:

  • JSX 以 DOM property 作為 Event attribute 命名
  • JSX 需使用 expression 註冊 event handler
  • 要使用 event.preventDefault 來取消預設行為
  • JSX 傳入 handler 的 event 參數為 Synthetic event

範例語法如下:

<element someDomEvent={callback}>...</element>

以下將針對每一點不同的地方做解釋。

以 DOM property 作為 event attribute 命名

HTML 中,event attribute 的名字會是 lowercase;相較之下,JavaScript 中,DOM event property 的名字則會是 camelCase。

由於 JSX 底層即為 JavaScript,因此其語法也是與 JavaScript 一致的。JSX 的 event attribute 名字會與 JavaScript 中的 DOM property 命名一樣都是用 camelCase 命名。

舉例來說,HTML onclick attribute 在 JSX 中會變成 JavaScript DOM property 的命名 onClick

// HTML
<a onclick="...">
  Activate Lasers
</a>
// JSX
<a onClick={...}>
  Activate Lasers
</a>

使用 JSX expression 註冊 event handler

另外一個與 HTML 語法的差異點是,HTML 會把 event handler 以 "function()" 的方式,把整個要執行的動作當作 string 傳入。相較之下,JSX 則是以 expression {function} 的方式,為 element 註冊 event handler。

這是因為 JSX 的本質依然是 JavaScript,JSX 只會覺得 "function()" 代表 string 而非 function,因此要直接帶入 function 本身才是正確的。

以剛剛的 onClick 為例:

// HTML
<a onclick="activateLasers()">
  Activate Lasers
</a>
// JSX
<a onClick={activateLasers}>
  Activate Lasers
</a>

要使用 event.preventDefault 來取消預設行為

同理,雖然 HTML 可以在 event handler string 裡面寫 return false 來取消預設 event 行為,但因為 JSX 的特性與 JavaScript 相同,因此要取消 event 的預設行為的話就必需要用 event.preventDefault

// HTML
<a onclick="activateLasers(); return false;">
  Activate Lasers
</a>
// JSX
<a href="https://google.com.tw" onClick={(event)=>{activateLasers(); event.preventDefault();}}>
  Activate Lasers
</a>

傳入 handler 的 event 參數為 Synthetic event

另外一個需要注意的不同處是,event 被觸發時,傳入 handler 的 event 並非是原生的 DOM event,而是 React 自己包裝過的 Synthetic event

接下來就來介紹何謂 Synthetic event。

Synthetic event

Synthetic event 為 React 包裝瀏覽器原生 event 後的產物。

Synthetic event 的介面(Interface)是參照 W3C spec 實作,因此使用方式與瀏覽器原生的 event 幾乎一模一致。

為何需要 Synthetic event

React 做出 Synthetic event 有一個很重要目的:讓 event 在不同瀏覽器上都有完全一致的行為。

這就是為何 React 特別將瀏覽器原生 event 包裹成 Synthetic event 的原因,如此開發者無需再為處理跨平台 event 行為不一的問題所困擾。

特性

細數下來,Synthetic event 有以下幾種特性:

  • 與原生 event 一樣,是根據 W3C 的 UI event spec 定義實作,因此介面與使用方式大致相同
  • 在各瀏覽器上的行為完全一致
  • 依然可以取用原生的 event
  • Synthetic event 有 Event pooling 的機制(< React 17)

以下將針對 可以取用原生的 eventEvent pooling 的機制 這兩點特別提出來介紹。

特性:依然可以取用原生的 event

在特殊狀況下,開發者如果需要使用到原生的 event 的話,還是可以透過 Synthetic event 中的 nativeEvent 屬性拿到原生 event:

// get browser's native event
event.nativeEvent

特性:Synthetic event 有 Event pooling 的機制(< React 17)

Synthetic event 有 event pooling 機制,也就是說為了最佳化效能,React 會讓不同的 event handler 之間共用一個 event object instance。

因為要保證不同 event handler 拿同一個 event object instance 時,讀取屬性的資料也不會混雜,因此在 event handler 同步內容執行結束後,event 內的所有屬性都會被初始化為 null

換句話說,在非同步的行為中拿 event 時,其屬性內容都會為 null,如下所示:

function onClick(event) {
  console.log(event.type); // "click"

  setTimeout(function() {
    console.log(event.type); // null
  }, 0);

  // Won't work. this.state.clickEvent will only contain null values.
  this.setState({clickEvent: event});

  // You can still export event properties.
  this.setState({eventType: event.type});
}
  • ?小提醒:Synthetic event 的 event pooling 機制將在 React 17 後被移除,因為對於現代瀏覽器來說,共用 object instance 提升效能的效果有限。

非同步使用 Synthetic event 內容

然而有時候功能會需要非同步的使用 event 內容。在這種狀況下,有兩種方式可以達到目的:

  • 使用 event.persist() 來保留其內容

    使用 event.persist() 後,React 就不會在同步行為結束後把 Synthetic event 內容清除,因此就可以在任何時候非同步的狀態下取用 persist 後的 event 了。

    範例如下:

    function onClick(event) {
      event.persist();
    
      setTimeout(function() {
        console.log(event.type); // "click"
        this.setState({eventType: event.type});
      }, 0);
    }
    

    可以看到 event.persist() 後,就算是非同步 setTimeout 完依然可以取用到 event 中的內容:event.type

  • 事先將需要的 event 內容存到變數中

    藉由事先將 event 的內容存到變數中,等到非同步行為時再取用也是一個方式。

    範例如下:

    function onClick(event) {
      const eventType = event.type; // => "click"
    
      setTimeout(function() {
        console.log(eventType); // => "click"
        this.setState({eventType});
      }, 0);
    }
    

    可以看到範例事先將需要非同步使用的內容存到另一個變數:eventType 中了,所以在 setTimeout callback 中,只要取用 eventType 就依然可以拿到正確的資料。

兩種方式都可以讓開發者非同步的使用 Synthetic event 內容,讀者可以端看使用情境決定要用哪種方式。

  • ?小提醒:Synthetic event 的 event pooling 機制將在 React 17 後被移除, 然而event.persist 這個函式還是會存在,只是它不會有任何功用而已(會是一個空函式)。

小結

在這個章節中,我們學習到了 JSX 處理 event 的語法,與 HTML 處理 event 主要有以下差異:

  • 使用 JSX expression 註冊 event handler
  • 以 DOM property 作為 event attribute 命名
  • 使用 JSX expression 註冊 event handler
  • JSX 傳入 handler 的 event 參數為 Synthetic event

除此之外,我們也學習到了 Synthetic event 是 React 包裝瀏覽器原生 event 後的產物。它是為了使各瀏覽器上的行為一致而存在。Synthetic event 有以下特性:

  • 與原生 event 一樣,是根據 W3C 的 UI event spec 定義實作,因此介面與使用方式大致相同
  • 在各瀏覽器上的行為完全一致
  • 依然可以取用原生的 event
  • Synthetic event 有 Event pooling 的機制(< React 17)

如果要讓 Synthetic event 可以非同步使用的話有兩種方法:

  • 使用 event.persist() 來保留其內容
  • 事先將需要的 event 內容存到變數中

下一篇我們將介紹正確使用 event 的方式。

參考資料


上一篇
I Want To Know React - Lifecycle 階段
下一篇
I Want To Know React - 正確使用 Event handler
系列文
I Want To Know React30

尚未有邦友留言

立即登入留言