React 事件處理 (Event Handling)
在 React 中處理事件(events),跟處理 DOM 事件很類似,但有些微的差異:
- HTML DOM 的 event handler(事件處理函式)屬性名稱都是小寫的,但 React 是 camelCase
- HTML DOM 的 event handler 屬性的值是一個字串,但在 JSX 中是一個函數
- 在 React 的事件處理函式中,你不能像在 DOM 可以
return false代表停掉瀏覽器預設行為,你需要明確的呼叫preventDefault()
像是在 HTML 中:
<button onclick="handleClick()">Click me</button>
而在 React 中:
<button onClick={handleClick}>Click me</button>
注意 React 的 onClick 是 camelCase,而且值是 {handleClick} 函式本身,不是字串。
基本事件處理
在 Function Component 中定義事件處理函式:
function Button() {
function handleClick() {
alert('你點擊了按鈕!')
}
return <button onClick={handleClick}>點我</button>
}
你也可以直接在 JSX 中使用 inline arrow function:
function Button() {
return <button onClick={() => alert('你點擊了按鈕!')}>點我</button>
}
簡單的邏輯可以用 inline function,但如果邏輯複雜,建議抽出成獨立的函式,讓程式碼更易讀。
Event Object
事件處理函式會接收一個 event object 作為參數,這是 React 的 SyntheticEvent:
function Form() {
function handleSubmit(event) {
// 阻止表單預設的提交行為
event.preventDefault()
console.log('表單已提交')
}
function handleChange(event) {
// 取得輸入欄位的值
console.log('輸入的值:', event.target.value)
}
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
<button type="submit">送出</button>
</form>
)
}
常用的 event object 屬性:
event.target:觸發事件的 DOM 元素event.target.value:表單元素的值event.preventDefault():阻止預設行為event.stopPropagation():阻止事件冒泡
合成事件 (SyntheticEvent)
React 有一層用來處理事件的機制,稱作 SyntheticEvent(合成事件)。SyntheticEvent 幫你處理好跨瀏覽器的問題(cross-browser compatibility),確保 React 的事件模型和 W3C 標準保持一致!
SyntheticEvent 包裝了原生的 DOM 事件,提供統一的介面。如果你需要存取原生的事件物件,可以透過 event.nativeEvent。
傳遞參數給事件處理函式
如果你需要傳遞額外的參數給事件處理函式,可以使用 arrow function:
function ItemList() {
const items = ['蘋果', '香蕉', '橘子']
function handleDelete(itemName) {
console.log('刪除:', itemName)
}
return (
<ul>
{items.map((item) => (
<li key={item}>
{item}
<button onClick={() => handleDelete(item)}>刪除</button>
</li>
))}
</ul>
)
}
如果同時需要 event object 和其他參數:
function handleClick(id, event) {
event.preventDefault()
console.log('ID:', id)
}
// 使用時
;<button onClick={(e) => handleClick(123, e)}>Click</button>
常用的事件類型
滑鼠事件
<div
onClick={() => console.log('click')}
onDoubleClick={() => console.log('double click')}
onMouseEnter={() => console.log('mouse enter')}
onMouseLeave={() => console.log('mouse leave')}
onMouseMove={(e) => console.log('position:', e.clientX, e.clientY)}
>
滑鼠事件區域
</div>
鍵盤事件
<input
onKeyDown={(e) => console.log('key down:', e.key)}
onKeyUp={(e) => console.log('key up:', e.key)}
onKeyPress={(e) => console.log('key press:', e.key)}
/>
常見用法 - 按 Enter 送出:
function SearchInput() {
const [query, setQuery] = useState('')
function handleKeyDown(event) {
if (event.key === 'Enter') {
console.log('搜尋:', query)
}
}
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="按 Enter 搜尋"
/>
)
}
表單事件
<input
onChange={(e) => console.log('value changed:', e.target.value)}
onFocus={() => console.log('focused')}
onBlur={() => console.log('blurred')}
/>
<form onSubmit={(e) => {
e.preventDefault();
console.log('form submitted');
}}>
{/* form content */}
</form>
焦點事件
<input onFocus={() => console.log('獲得焦點')} onBlur={() => console.log('失去焦點')} />
事件冒泡與捕獲
React 的事件會像 DOM 事件一樣冒泡(bubble)。你可以使用 event.stopPropagation() 來阻止事件冒泡:
function Parent() {
return (
<div onClick={() => console.log('Parent clicked')}>
<button
onClick={(e) => {
e.stopPropagation()
console.log('Button clicked')
}}
>
點我
</button>
</div>
)
}
點擊按鈕時,只會印出 "Button clicked",不會觸發 parent 的 onClick。
捕獲階段的事件
如果你需要在捕獲階段處理事件,可以使用 onClickCapture 等 Capture 結尾的事件:
<div onClickCapture={() => console.log('Capture phase')}>
<button onClick={() => console.log('Bubble phase')}>Click</button>
</div>
實際範例:開關按鈕
import { useState } from 'react'
function Toggle() {
const [isOn, setIsOn] = useState(false)
function handleClick() {
setIsOn((prev) => !prev)
}
return (
<button
onClick={handleClick}
style={{
backgroundColor: isOn ? '#4CAF50' : '#f44336',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
{isOn ? '開' : '關'}
</button>
)
}
實際範例:表單輸入
import { useState } from 'react'
function LoginForm() {
const [formData, setFormData] = useState({
username: '',
password: '',
})
function handleChange(event) {
const { name, value } = event.target
setFormData((prev) => ({
...prev,
[name]: value,
}))
}
function handleSubmit(event) {
event.preventDefault()
console.log('登入資料:', formData)
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="username">帳號:</label>
<input
type="text"
id="username"
name="username"
value={formData.username}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="password">密碼:</label>
<input
type="password"
id="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
</div>
<button type="submit">登入</button>
</form>
)
}