在 React 前端開發中,我們會使用不同的 React 事件處理器(Event Handlers)來回應使用者的各種操作行為。與原生 HTML 使用全小寫屬性(例如 onclick)不同,React 的事件屬性一律採用小駝峰命名法(camelCase,例如 onClick、onChange),並直接傳遞 JavaScript 函式作為處理器。本篇整理開發最常使用的事件速查表、合成事件(SyntheticEvent)操作方式,以及綁定事件時的常見注意事項。
一、React 常見事件處理器(Event Handlers)速查表
以下整理 React 元件中最常使用的滑鼠、表單、焦點、鍵盤與捲動事件:
| 事件屬性名稱 | 觸發時機說明 | 常見前端使用情境 |
|---|---|---|
onClick | 當元素被滑鼠點擊或觸控輕觸時觸發 | 按鈕送出、展開選單、切換狀態、開啟 Modal |
onChange | 當表單輸入元素的值發生變化時即時觸發 | 文字輸入框(input)、下拉選單(select)、核取方塊受控元件同步 |
onSubmit | 當表單(form)送出時觸發 | 攔截表單提交並搭配 e.preventDefault() 改由前端發送 API |
onFocus | 當元素獲得焦點時觸發 | 輸入框聚焦時高亮外框樣式、展開搜尋建議清單 |
onBlur | 當元素失去焦點時觸發 | 使用者離開輸入框時執行欄位格式驗證(如 Email 格式檢查) |
onMouseEnter | 當滑鼠游標移入元素範圍時觸發(不冒泡) | 顯示懸停提示框(Tooltip)、切換卡片預覽狀態 |
onMouseLeave | 當滑鼠游標離開元素範圍時觸發(不冒泡) | 隱藏懸停提示框、還原按鈕或卡片預設樣式 |
onKeyDown / onKeyUp | 當鍵盤按鍵按下或放開時觸發 | 監聽 Enter 鍵送出訊息、Escape 鍵關閉視窗(註:舊版 onKeyPress 已棄用) |
onDragStart / onDrop | 當拖曳開始、經過(onDragOver)與放下時觸發 | 實作看板卡片拖放排序、檔案拖曳上傳區塊 |
onScroll | 當可捲動容器的捲軸位置改變時觸發 | 監聽列表捲動位置、實作無限捲動載入(Infinite Scroll)或回到頂部按鈕 |
這些事件處理器可以根據元件的互動需求自由組合。接著我們透過實際程式碼範例,說明如何在元件中宣告並綁定事件函式。
二、基本事件綁定與 SyntheticEvent 合成事件物件
在 React 中綁定事件時,會將事件處理函式傳入 JSX 屬性(通常命名為 handle 開頭,例如 handleClick、handleSubmit)。React 會自動傳入一個跨瀏覽器標準化的合成事件物件 e(SyntheticEvent),其介面與原生 DOM 事件一致,支援呼叫 e.preventDefault() 與 e.stopPropagation():
import { useState } from 'react';
export default function LoginForm() {
const [email, setEmail] = useState('');
// 處理輸入框文字變更
const handleEmailChange = (e) => {
setEmail(e.target.value);
};
// 處理表單送出
const handleFormSubmit = (e) => {
// 阻止瀏覽器預設的表單換頁重整行為
e.preventDefault();
console.log('準備送出的 Email:', email);
};
return (
<form onSubmit={handleFormSubmit}>
<label htmlFor="user-email">電子信箱:</label>
<input
id="user-email"
type="email"
value={email}
onChange={handleEmailChange}
placeholder="請輸入 Email"
/>
<button type="submit">登入</button>
</form>
);
}
三、如何在事件處理器中傳遞自訂參數?(避免無限迴圈陷阱)
在渲染列表時(例如刪除或編輯特定 id 的項目),我們常需要把 item.id 傳進 onClick 處理函式中。新手最常犯的錯誤是直接在 JSX 寫成 onClick={handleDelete(item.id)},這會導致元件在渲染當下就「立刻呼叫函式」,甚至引發 Too many re-renders 無限迴圈。
正確的做法是使用箭頭函式 () => handleDelete(item.id) 包裹,確保只有在使用者真正點擊時才執行:
export default function TodoList() {
const todos = [
{ id: 101, title: '整理 API 文件' },
{ id: 102, title: '修正列表 Key 警告' },
];
// 同時接收自訂參數 id 與合成事件物件 e
const handleDelete = (id, e) => {
e.stopPropagation(); // 阻止點擊事件向上冒泡觸發父層列點擊
console.log('準備刪除項目 ID:', id);
};
return (
<ul>
{todos.map((item) => (
<li key={item.id}>
<span>{item.title}</span>
{/* ✅ 正確寫法:使用箭頭函式延遲至點擊時才呼叫 */}
<button type="button" onClick={(e) => handleDelete(item.id, e)}>
刪除
</button>
</li>
))}
</ul>
);
}
💡 列表渲染排錯補充:使用
.map()搭配事件處理器渲染列表時,若遇到 Console 提示子元件key重複的紅字警告,可參考站內另一篇 Encountered Two Children with the Same Key 原因與唯一 Key 解法。
四、React 事件處理的 3 個實務注意事項
- 傳遞函式參照而非呼叫結果:無參數時寫
onClick={handleClick},切勿加上小括號寫成onClick={handleClick()}。 onKeyPress已正式棄用:若需監聽鍵盤輸入(例如按下Enter送出搜尋),請統一使用onKeyDown並檢查if (e.key === 'Enter')。- 事件冒泡與
e.stopPropagation():當卡片外層div綁定了點擊導頁onClick,而卡片內部的「收藏按鈕」也有自己的onClick時,記得在內部按鈕的事件函式中呼叫e.stopPropagation(),避免點擊收藏時連帶觸發外層卡片跳轉。
發佈留言