React 事件处理
用户点按钮、敲键盘、提交表单——这些"交互"怎么让组件响应?答案就是 React 的事件系统。它的写法和原生 DOM 很像,但有几个关键不同,这一章我们一次讲清。
1. 基本写法:驼峰命名 + 花括号
React 事件的写法和原生 HTML 几乎一样,两个区别:事件名用驼峰(onClick 不是 onclick),handler 用花括号传函数(不是字符串)。
// React 事件用驼峰命名:onclick → onClick,onchange → onChange
function Button() {
const handleClick = () => {
console.log("按钮被点了");
};
return <button onClick={handleClick}>点我</button>;
}
// 或者直接写箭头函数(短小逻辑时方便)
function Button() {
return <button onClick={() => console.log("点了")}>点我</button>;
}
// 对比原生 DOM 写法:
// HTML: <button onclick="handleClick()">点我</button>
// React: <button onClick={handleClick}>点我</button>(注意没有括号)注意一个常见错误:写 onClick={handleClick()}(带括号)会立刻执行函数,而不是点击时才执行。正确写法是 onClick={handleClick}(传函数本身),或用箭头函数包一层 onClick={() => handleClick()}。
2. 合成事件对象
事件处理函数会自动收到一个参数 e,它是 React 的合成事件(SyntheticEvent)。它包装了原生事件,API 几乎一样,但帮你抹平了浏览器差异:
function Input() {
// 第一个参数 e 是"合成事件"(SyntheticEvent)
// 它包装了原生事件,API 几乎一样,但跨浏览器兼容
const handleChange = (e) => {
console.log("输入的内容:", e.target.value);
// e.target 是触发事件的 DOM 元素(这里是 input)
};
const handleClick = (e) => {
console.log("点击类型:", e.type); // "click"
console.log("鼠标坐标:", e.clientX, e.clientY);
e.preventDefault(); // 阻止默认行为(如 a 标签跳转)
e.stopPropagation(); // 阻止事件冒泡
};
return (
<>
<input onChange={handleChange} />
<a href="https://example.com" onClick={handleClick}>
不会跳转的链接
</a>
</>
);
}日常最常用的就是 e.target.value(输入框的值)、e.preventDefault()(阻止默认行为)、e.stopPropagation()(阻止冒泡)。表单提交的 onSubmit 一定要加 e.preventDefault(),否则页面会刷新,React 的 state 也全没了。
3. 给事件 handler 传参
这是新手最常卡住的地方:onClick 的 handler 默认只接收事件对象,但我想把列表项的 id 传进去怎么办?
function TodoList({ todos, onDelete }) {
// ❌ 错误:这样会立刻执行,而不是点击时执行
// <button onClick={onDelete(todo.id)}>删除</button>
// ✅ 方式 1:用箭头函数包一层(最常用)
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => onDelete(todo.id)}>删除</button>
</li>
))}
</ul>
);
}
// ✅ 方式 2:用 .bind(事件对象会作为第二个参数传入)
<button onClick={onDelete.bind(null, todo.id)}>删除</button>
// ✅ 方式 3:用 data-* 属性(更接近原生写法)
<button data-id={todo.id} onClick={handleClick}>删除</button>
// 然后在 handleClick 里读 e.currentTarget.dataset.id最常用的是箭头函数包一层:onClick={() => onDelete(todo.id)}。这样点击时才执行箭头函数,内部调用 onDelete 并传入 id。清晰、好读、不会出错。
4. 常用事件类型
React 支持几乎所有原生事件,常用的有:
function Form() {
// click:鼠标点击
const handleClick = () => console.log("点击");
// change:输入框内容变化(每按一个键都触发)
const handleChange = (e) => console.log(e.target.value);
// submit:表单提交
const handleSubmit = (e) => {
e.preventDefault(); // ⚠️ 必加,否则页面会刷新
console.log("提交表单");
};
// mouseEnter / mouseLeave:鼠标进入/离开(不冒泡版)
const handleEnter = () => console.log("鼠标进来了");
// focus / blur:获得/失去焦点
const handleFocus = () => console.log("聚焦了");
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} onFocus={handleFocus} />
<button onClick={handleClick} type="submit">提交</button>
<div onMouseEnter={handleEnter}>悬停区域</div>
</form>
);
}- 鼠标:
onClick、onDoubleClick、onMouseEnter、onMouseLeave - 键盘:
onKeyDown、onKeyUp、onKeyPress(查看e.key知道按了哪个键) - 表单:
onChange(输入变化)、onSubmit(提交)、onFocus、onBlur - 剪贴板:
onCopy、onPaste
注意 onChange 在 React 里和原生不同:原生 input 的 change 是失焦才触发,React 的 onChange 是每次输入都触发(更像原生的 oninput)。这是 React 为了实时受控刻意设计的。
5. 合成事件 vs 原生事件
React 的事件绑定机制和原生有个重要区别:
- 原生:每个元素各自绑定事件监听器,事件触发时从目标元素冒泡上去。
- React:所有事件统一绑定在根节点(document 或 root),触发时 React 用映射找到对应组件的 handler——这叫事件代理。好处是性能好、内存少,你写起来没区别。
实践中几乎感觉不到差别,但要知道为什么有时候 e.stopPropagation() 不生效——它只能阻止 React 合成事件的冒泡,挡不住原生监听器。
6. 综合示例:计数器
把事件和 state 组合起来,就是一个完整的交互组件:
import { useState } from "react";
// 经典例子:把事件、state 组合起来
function Counter() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(0);
return (
<div>
<p>当前计数:{count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={reset}>重置</button>
</div>
);
}事件 → set state → 重渲染 → UI 更新,这就是 React 交互的完整闭环。任何复杂的 React 应用,本质都是这个循环的放大版。
小结
React 事件用驼峰命名 + 花括号传函数。事件对象是合成事件,常用 e.target.value 和 e.preventDefault()。给 handler 传参用箭头函数包一层。下一篇看 React 怎么渲染列表——列表与 key。
← 上一篇 State 与 useState
下一篇 列表渲染与 key →