如 HTML 一樣,JSX 也提供了處理 DOM event 的方法。就讓我們來了解一下語法吧!
JSX 註冊 event 語法與 HTML 註冊 event 的語法十分相似,只要把 function 加入 DOM element attribute 的 value 中即可,但因為 JSX 與 HTML 特性不同,因此語法上還是有些區別。
JSX 註冊 event 的語法只是 JSX 基礎語法的應用而已。
就像初探 JSX 與 JSX 語法章節提到的,JSX 底層即為 JavaScript,因此 JSX 的語法特性比起 HTML 還是會更接近 JavaScript。只要熟悉這個觀念,並了解 JSX 的 expression 與 attribute 命名方式的話,就可以輕鬆寫出註冊 event 的語法。
JSX 與 HTML 在註冊 event 的語法上會有以下這幾項差異:
event.preventDefault
來取消預設行為範例語法如下:
<element someDomEvent={callback}>...</element>
以下將針對每一點不同的地方做解釋。
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>
另外一個與 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>
另外一個需要注意的不同處是,event 被觸發時,傳入 handler 的 event 並非是原生的 DOM event,而是 React 自己包裝過的 Synthetic event。
接下來就來介紹何謂 Synthetic event。
Synthetic event 為 React 包裝瀏覽器原生 event 後的產物。
Synthetic event 的介面(Interface)是參照 W3C spec 實作,因此使用方式與瀏覽器原生的 event 幾乎一模一致。
React 做出 Synthetic event 有一個很重要目的:讓 event 在不同瀏覽器上都有完全一致的行為。
這就是為何 React 特別將瀏覽器原生 event 包裹成 Synthetic event 的原因,如此開發者無需再為處理跨平台 event 行為不一的問題所困擾。
細數下來,Synthetic event 有以下幾種特性:
以下將針對 可以取用原生的 event 與 Event pooling 的機制 這兩點特別提出來介紹。
在特殊狀況下,開發者如果需要使用到原生的 event 的話,還是可以透過 Synthetic event 中的 nativeEvent
屬性拿到原生 event:
// get browser's native event
event.nativeEvent
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});
}
然而有時候功能會需要非同步的使用 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 內容,讀者可以端看使用情境決定要用哪種方式。
event.persist
這個函式還是會存在,只是它不會有任何功用而已(會是一個空函式)。在這個章節中,我們學習到了 JSX 處理 event 的語法,與 HTML 處理 event 主要有以下差異:
除此之外,我們也學習到了 Synthetic event 是 React 包裝瀏覽器原生 event 後的產物。它是為了使各瀏覽器上的行為一致而存在。Synthetic event 有以下特性:
如果要讓 Synthetic event 可以非同步使用的話有兩種方法:
event.persist()
來保留其內容下一篇我們將介紹正確使用 event 的方式。