iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0
Modern Web

30 days of React 系列 第 12

Day 12 - 在React 處理事件(下)

  • 分享至 

  • xImage
  •  

接著來學習在React當中處理事件,主要會提到React當中的事件傳遞機制,內容如下:

  • React當中的事件傳遞
  • 停止事件傳遞
  • React當中的捕獲
  • 預防預設行為

React的事件傳遞

在原生JavaScript中,事件傳遞有兩個階段:事件捕獲和事件冒泡。事件捕獲是自頂向下的,而事件冒泡是自底向上的。而在React中,使用的是合成事件系統(Synthetic Event System),並且在React元素樹中,事件從最內層的元素冒泡到最外層的元素。

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <button onClick={() => alert('Playing!')}>
        Play Movie
      </button>
      <button onClick={() => alert('Uploading!')}>
        Upload Image
      </button>
    </div>
  );
}

譬如,在這個Toolbar 當中,有二個按鈕,當點擊了任一其中一個按鈕「 Play Movie」或是「Upload Image」時,首先會先接收到該按鈕(事件本身)的通知「Playing!」或是「Uploading!」,再來還會接收到另一個通知也就是父容器的「You clicked on the toolbar!」,顯示到也點擊到Toolbar 本身。這也就是React預設的事件傳遞邏輯。

但也許我們不需要二次的通知,可以怎麼做呢?

停止事件傳遞

function Button({ onClick, children }) {
  return (
    <button onClick={e => {
      e.stopPropagation();
      onClick();
    }}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <Button onClick={() => alert('Playing!')}>
        Play Movie
      </Button>
      <Button onClick={() => alert('Uploading!')}>
        Upload Image
      </Button>
    </div>
  );
}

我們可以透過event object 來停止事件的傳遞,在button 元素的onClick 當中呼叫 e.stopPropagation() 即可讓事件傳遞停留在子元件,而不會讓父元件也跳出一次通知了。

讓我們再一次看看這邊發生了什麼事:

[User Clicks Button] #使用者點擊按鈕

↓

[React calls onClick handler passed to <button>] #React 呼叫onClick事件處理器並傳遞到<button>

↓

[Button onClick Handler]
   - Calls e.stopPropagation()  # 阻止事件進一步傳播
   - Calls onClick function (prop)  # 呼叫從Toolbar傳遞過來的onClick函數

↓

[Toolbar onClick Function]
   - Displays the button's own alert  # 顯示按鈕自己的警告框

↓

[Parent <div> onClick Handler (Not Executed)]
   - Since propagation was stopped, this handler does not run  # 由於事件傳播被阻止,這個處理器不執行

React當中的捕獲

雖然React當中事件傳遞的預設是冒泡,有其他方法能夠操作捕獲嗎?

我們可以透過追加捕獲的設定來達到這個目標

<div onClickCapture={() => { /* this runs first */ }}>
  <button onClick={e => e.stopPropagation()} />
  <button onClick={e => e.stopPropagation()} />
</div>

預防預設行為

我們可以透過 e.preventDefault() 來預防瀏覽器的預設行為,譬如,在button存在於form當中時,每當點擊整個頁面的預設行為為reload,透過追加e.preventDefault() 可以防止這樣的reload。

export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault();
      alert('Submitting!');
    }}>
      <input />
      <button>Send</button>
    </form>
  );
}

停止傳播VS防止預設

這二者容易混淆,需要多加留意

  • e.stopPropagation: 取消事件傳遞
  • e.preventDefault: 取消瀏覽器的預設行為

附錄補充:合成事件系統(Synthetic Event System)

在 React 中,事件處理被封裝成合成事件(Synthetic Events)。合成事件是 React 提供的一個抽象層,用於處理跨不同瀏覽器的事件處理。React 的合成事件系統在背後處理了各種瀏覽器差異,以確保事件處理在各種環境中保持一致。

合成事件的一些特點和好處包括:

  1. 瀏覽器差異抹平:React 使開發者無需擔心不同瀏覽器之間的事件處理差異。可以使用相同的事件處理程式碼,而不必擔心跨瀏覽器兼容性。
  2. 性能優化:React 使用事件委派(event delegation)來優化性能。它將事件處理程序附加到根元素(通常是 <div><body>),而不是每個元素都附加一個事件處理程序。這可以減少內存使用和提高性能。
  3. 合成事件池:React 使用事件池(event pool)來管理合成事件的創建和回收。這有助於減少垃圾回收的負擔,提高性能。
  4. 事件代理:React 通過捕獲事件冒泡的方式處理事件,可以在適當的組件處理事件,而不需要在每個元素上都添加事件處理程序。

參考資料

  • React 官方文件 - Responding to Events

上一篇
Day 11 - 在React處理事件(上)
下一篇
Day 13 - 在React中使用state
系列文
30 days of React 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言