响应事件:事件处理函数、传参与阻止冒泡
到目前为止,你学会了如何用组件和数据渲染静态的 UI。但一个真正有用的应用,必须能够倾听并响应用户的操作:点击按钮、键盘输入、鼠标滑动、表单提交。
在 React 中,你可以向 JSX 元素添加 事件处理函数(Event Handlers) 来响应这些用户交互。
1. 添加事件处理函数
添加事件处理函数只需两步:
- 在你的组件函数内部声明一个函数(通常命名为
handle加上事件名称,比如handleClick、handleChange); - 将这个函数作为 prop 传递给对应的 JSX 元素(如
onClick)。
export default function Button() {
function handleClick() {
alert('你点击了我!');
}
return (
<button onClick={handleClick}>
点我
</button>
);
}
也可以使用简短的内联箭头函数直接写在 JSX 里:
<button onClick={() => {
alert('你点击了我!');
}}>
点我
</button>
2. 致命区别:传递函数 vs 调用函数
这是每个 React 初学者最常踩的一个坑!请仔细对比以下两行代码:
// ✅ 正确:传递函数
<button onClick={handleClick}>
// ❌ 错误:调用函数!
<button onClick={handleClick()}>
为什么加了小括号就会出大问题?
onClick={handleClick}(无小括号):这是在告诉 React:“请把这个函数保存起来,只有当用户真正点击按钮时,再去调用它!”;onClick={handleClick()}(有小括号):小括号在 JavaScript 中代表立即执行函数!当组件在初次渲染时,代码一读到这里,handleClick()就会在渲染期间当场执行(弹窗立刻弹出)。而且,如果该函数里有setState,它会导致立刻触发重新渲染,然后再次立即执行,直接导致死循环崩溃!
记忆口诀:只把函数本身的名字交上去,不要在绑定时私自按下执行的小括号。
3. 在事件中传递自定义参数
如果你需要向处理函数中传入参数(例如当前项的 ID),必须用一个外层箭头函数把它包装起来:
// ❌ 错误:这会在渲染时立刻调用并传入 5
<button onClick={handleClick(5)}>
// ✅ 正确:定义一个箭头函数,点击时才调用 handleClick(5)
<button onClick={() => handleClick(5)}>
4. 事件传播(冒泡机制)与阻止冒泡
事件处理程序不仅会捕获直接子元素的交互,还会响应子元素深处冒上来的事件。这被称为 事件冒泡(Event Propagation / Bubbling)。
冒泡场景分析
观察下面的工具栏代码:
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('你点击了工具栏空白处!');
}}>
<button onClick={() => alert('正在播放电影!')}>
播放电影
</button>
<button onClick={() => alert('正在上传图片!')}>
上传图片
</button>
</div>
);
}
如果你点击了“播放电影”按钮,你会发现屏幕上先后弹出了两个弹窗:
- 首先弹出“正在播放电影!”;
- 紧接着弹出“你点击了工具栏空白处!”。
因为按钮被点击后,事件顺着 DOM 树向上扩散给了它的父级 <div>!
使用 e.stopPropagation() 阻止冒泡
事件处理函数会收到一个浏览器事件对象,通常简写为 e。调用 e.stopPropagation() 可以命令事件“停在原地,不要再往上扩散了”:
function Button({ onClick, children }) {
return (
<button onClick={(e) => {
e.stopPropagation(); // 阻止事件向上冒泡到父级 Toolbar!
onClick();
}}>
{children}
</button>
);
}
现在,点击按钮时,只会触发按钮自身的行为,父级 Toolbar 的点击事件被完美隔离!
5. 阻止默认行为(e.preventDefault)
某些特定浏览器事件具有默认的网页行为。最典型的就是表单内部的提交按钮:
当点击带有 <form> 的提交按钮时,浏览器默认会刷新整个网页并提交表单数据。
如果你希望在单页应用里通过 Ajax/Fetch 自己处理提交,可以使用 e.preventDefault() 阻止这一默认行为:
export default function SignupForm() {
function handleSubmit(e) {
e.preventDefault(); // 阻止浏览器默认的整个网页刷新行为!
alert('正在安全提交数据,无需刷新页面!');
}
return (
<form onSubmit={handleSubmit}>
<input placeholder="请输入用户名" />
<button type="submit">注册</button>
</form>
);
}
总结
- 通过形如
onClick、onSubmit的驼峰属性绑定事件; - 始终传递函数本身(
onClick={handleClick}),切勿写成立即执行(onClick={handleClick()}); - 传递额外参数时使用内联箭头函数
onClick={() => handleClick(id)}; - 使用
e.stopPropagation()阻止点击事件向上层冒泡; - 使用
e.preventDefault()阻止表单提交等浏览器默认行为。