跳到主要内容

阶段5:事件处理

🎯 学习目标

掌握React中的事件绑定方式,理解与原生JS的区别,学会处理常见事件(点击、输入、提交等)。


一、React事件 vs 原生JS事件

语法对比

// 原生JavaScript
<button onclick="handleClick()">点击</button>

// React
<button onClick={handleClick}>点击</button>

3个关键区别:

特性原生JSReact
命名小写 onclick驼峰 onClick
传值字符串 "handleClick()"函数 {handleClick}
阻止默认行为return false调用 event.preventDefault()

二、基本事件绑定

方式1:直接定义函数(推荐)

import { useState } from 'react';

function ClickDemo() {
const [count, setCount] = useState(0);

// 定义事件处理函数
const handleClick = () => {
setCount(count + 1);
console.log('按钮被点击了!');
};

return (
<div>
<p>点击次数: {count}</p>
{/* 传递函数名,不要加括号! */}
<button onClick={handleClick}>点击我</button>
</div>
);
}

⚠️ 常见错误:

// ❌ 错误:加了括号,函数会立即执行
<button onClick={handleClick()}>点击</button>

// ✅ 正确:只传函数名
<button onClick={handleClick}>点击</button>

// ✅ 或者用箭头函数包裹
<button onClick={() => handleClick()}>点击</button>

方式2:内联箭头函数(简单逻辑用)

function InlineDemo() {
const [count, setCount] = useState(0);

return (
<div>
<p>{count}</p>
{/* 直接写逻辑 */}
<button onClick={() => setCount(count + 1)}>
+1
</button>
<button onClick={() => setCount(count - 1)}>
-1
</button>
<button onClick={() => setCount(0)}>
重置
</button>
</div>
);
}

什么时候用内联?

  • ✅ 逻辑简单(1-2行代码)
  • ❌ 逻辑复杂(多行代码) → 定义单独函数

三、常见事件类型

1. 点击事件 (onClick)

function ButtonEvents() {
const handleClick = () => alert('点击!');
const handleDoubleClick = () => alert('双击!');
const handleRightClick = (e) => {
e.preventDefault(); // 阻止默认右键菜单
alert('右键点击!');
};

return (
<div>
<button onClick={handleClick}>点击</button>
<button onDoubleClick={handleDoubleClick}>双击</button>
<button onContextMenu={handleRightClick}>右键点击</button>
</div>
);
}

2. 输入事件 (onChange)

import { useState } from 'react';

function InputEvents() {
const [text, setText] = useState('');

const handleChange = (event) => {
console.log('当前值:', event.target.value);
setText(event.target.value);
};

return (
<div>
<input
type="text"
value={text}
onChange={handleChange}
placeholder="输入点什么..."
/>
<p>你输入的: {text}</p>
</div>
);
}

event对象常用属性:

  • event.target: 触发事件的元素
  • event.target.value: 输入框的值
  • event.target.checked: 复选框的选中状态

3. 表单提交事件 (onSubmit)

import { useState } from 'react';

function LoginForm() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');

const handleSubmit = (event) => {
event.preventDefault(); // ⚠️ 阻止默认刷新行为,很重要!

console.log('提交的数据:', { username, password });
alert(`欢迎 ${username} 登录!`);

// 清空表单
setUsername('');
setPassword('');
};

return (
<form onSubmit={handleSubmit} style={{ padding: '20px' }}>
<div style={{ marginBottom: '10px' }}>
<label>用户名: </label>
<input
type="text"
value={username}
onChange={e => setUsername(e.target.value)}
required
/>
</div>

<div style={{ marginBottom: '10px' }}>
<label>密码: </label>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
required
/>
</div>

<button type="submit">登录</button>
</form>
);
}

export default LoginForm;

关键点:

  • onSubmit 绑定在 <form> 标签上
  • 必须调用 event.preventDefault() 阻止页面刷新
  • 点击提交按钮或按回车都会触发

4. 键盘事件 (onKeyDown / onKeyUp)

import { useState } from 'react';

function KeyboardDemo() {
const [message, setMessage] = useState('');

const handleKeyDown = (event) => {
// 按下Enter发送消息
if (event.key === 'Enter') {
alert(`发送消息: ${message}`);
setMessage('');
}

// 按下Escape清空
if (event.key === 'Escape') {
setMessage('');
}
};

return (
<div style={{ padding: '20px' }}>
<p>提示: 按Enter发送,Escape清空</p>
<input
type="text"
value={message}
onChange={e => setMessage(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="输入消息..."
style={{ width: '300px', padding: '10px' }}
/>
</div>
);
}

export default KeyboardDemo;

常用的event.key值:

  • 'Enter': 回车
  • 'Escape': ESC键
  • 'ArrowUp' / 'ArrowDown': 上下箭头
  • 'a', 'b', '1', '2': 字母和数字

5. 鼠标移动事件

import { useState } from 'react';

function MouseTracker() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [isInside, setIsInside] = useState(false);

const handleMouseMove = (event) => {
setPosition({
x: event.clientX, // 鼠标X坐标
y: event.clientY // 鼠标Y坐标
});
};

return (
<div
onMouseMove={handleMouseMove}
onMouseEnter={() => setIsInside(true)} // 鼠标进入
onMouseLeave={() => setIsInside(false)} // 鼠标离开
style={{
width: '400px',
height: '300px',
border: '2px solid #333',
position: 'relative',
backgroundColor: isInside ? '#e3f2fd' : 'white'
}}
>
<p>鼠标位置: X: {position.x}, Y: {position.y}</p>
<p>鼠标在区域内: {isInside ? '是' : '否'}</p>
</div>
);
}

export default MouseTracker;

四、事件传参的技巧

问题:如何给事件处理函数传额外参数?

import { useState } from 'react';

function ParameterDemo() {
const [selectedColor, setSelectedColor] = useState('gray');

// 方式1:事件处理函数接收参数
const handleSelectColor = (color) => {
console.log('选择的颜色:', color);
setSelectedColor(color);
};

return (
<div style={{ padding: '20px' }}>
<p>当前颜色: {selectedColor}</p>

{/* ✅ 用箭头函数包裹,传递参数 */}
<button onClick={() => handleSelectColor('red')}>
红色
</button>
<button onClick={() => handleSelectColor('green')}>
绿色
</button>
<button onClick={() => handleSelectColor('blue')}>
蓝色
</button>

{/* 显示当前颜色的方块 */}
<div style={{
width: '100px',
height: '100px',
backgroundColor: selectedColor,
marginTop: '20px',
border: '2px solid #333'
}}></div>
</div>
);
}

export default ParameterDemo;

实战案例:删除列表项

import { useState } from 'react';

function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: '学习React' },
{ id: 2, text: '做项目' },
{ id: 3, text: '写总结' }
]);

// 删除函数:接收id参数
const handleDelete = (id) => {
const newTodos = todos.filter(todo => todo.id !== id);
setTodos(newTodos);
};

return (
<div style={{ padding: '20px' }}>
<h3>待办事项</h3>
<ul>
{todos.map(todo => (
<li key={todo.id} style={{ marginBottom: '10px' }}>
{todo.text}
{/* 传递id给删除函数 */}
<button
onClick={() => handleDelete(todo.id)}
style={{ marginLeft: '10px', color: 'red' }}
>
删除
</button>
</li>
))}
</ul>
</div>
);
}

export default TodoList;

五、阻止默认行为和事件冒泡

1. 阻止默认行为 (preventDefault)

function LinkDemo() {
const handleClick = (event) => {
event.preventDefault(); // 阻止链接跳转
alert('链接被拦截了!');
};

return (
<a href="https://www.google.com" onClick={handleClick}>
点我(不会跳转)
</a>
);
}

常见使用场景:

  • 表单提交:阻止页面刷新
  • 链接点击:阻止跳转
  • 右键菜单:阻止默认菜单

2. 阻止事件冒泡 (stopPropagation)

function BubbleDemo() {
const handleParentClick = () => {
alert('父元素被点击');
};

const handleChildClick = (event) => {
event.stopPropagation(); // 阻止事件向上传递
alert('子元素被点击');
};

return (
<div
onClick={handleParentClick}
style={{
padding: '40px',
backgroundColor: '#f0f0f0',
cursor: 'pointer'
}}
>
父元素
<button
onClick={handleChildClick}
style={{ display: 'block', marginTop: '10px' }}
>
子元素(不会触发父元素事件)
</button>
</div>
);
}

六、综合实战:交互式问卷

目标:一个简单的调查问卷

创建 src/Survey.js:

import { useState } from 'react';

function Survey() {
const [name, setName] = useState('');
const [age, setAge] = useState('');
const [hobby, setHobby] = useState('');
const [agree, setAgree] = useState(false);
const [submitted, setSubmitted] = useState(false);

const handleSubmit = (event) => {
event.preventDefault();

// 验证
if (!name || !age || !hobby) {
alert('请填写完整信息!');
return;
}

if (!agree) {
alert('请同意协议!');
return;
}

// 提交成功
setSubmitted(true);
};

const handleReset = () => {
setName('');
setAge('');
setHobby('');
setAgree(false);
setSubmitted(false);
};

// 提交后显示结果
if (submitted) {
return (
<div style={{ padding: '30px', maxWidth: '500px', margin: '0 auto' }}>
<h2 style={{ color: '#4CAF50' }}>✓ 提交成功!</h2>
<div style={{
backgroundColor: '#f9f9f9',
padding: '20px',
borderRadius: '8px',
marginTop: '20px'
}}>
<p><strong>姓名:</strong> {name}</p>
<p><strong>年龄:</strong> {age}</p>
<p><strong>爱好:</strong> {hobby}</p>
</div>
<button
onClick={handleReset}
style={{
marginTop: '20px',
padding: '10px 20px',
backgroundColor: '#2196F3',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
重新填写
</button>
</div>
);
}

// 未提交:显示表单
return (
<div style={{ padding: '30px', maxWidth: '500px', margin: '0 auto' }}>
<h2>用户调查问卷</h2>

<form onSubmit={handleSubmit}>
{/* 姓名 */}
<div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>
姓名:
</label>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder="请输入姓名"
style={{
width: '100%',
padding: '10px',
fontSize: '16px',
border: '1px solid #ddd',
borderRadius: '5px'
}}
/>
</div>

{/* 年龄 */}
<div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>
年龄:
</label>
<input
type="number"
value={age}
onChange={e => setAge(e.target.value)}
placeholder="请输入年龄"
min="1"
max="120"
style={{
width: '100%',
padding: '10px',
fontSize: '16px',
border: '1px solid #ddd',
borderRadius: '5px'
}}
/>
</div>

{/* 爱好(下拉框) */}
<div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>
爱好:
</label>
<select
value={hobby}
onChange={e => setHobby(e.target.value)}
style={{
width: '100%',
padding: '10px',
fontSize: '16px',
border: '1px solid #ddd',
borderRadius: '5px'
}}
>
<option value="">请选择</option>
<option value="运动">运动</option>
<option value="阅读">阅读</option>
<option value="音乐">音乐</option>
<option value="编程">编程</option>
</select>
</div>

{/* 复选框 */}
<div style={{ marginBottom: '20px' }}>
<label>
<input
type="checkbox"
checked={agree}
onChange={e => setAgree(e.target.checked)}
style={{ marginRight: '8px' }}
/>
我同意用户协议
</label>
</div>

{/* 按钮 */}
<div style={{ display: 'flex', gap: '10px' }}>
<button
type="submit"
style={{
padding: '12px 24px',
backgroundColor: '#4CAF50',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer',
fontSize: '16px'
}}
>
提交
</button>

<button
type="button"
onClick={handleReset}
style={{
padding: '12px 24px',
backgroundColor: '#f44336',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer',
fontSize: '16px'
}}
>
重置
</button>
</div>
</form>
</div>
);
}

export default Survey;

运行结果:

  1. 填写表单(姓名、年龄、爱好、同意协议)
  2. 点提交 → 显示填写的信息
  3. 点重新填写 → 回到表单

✅ 阶段5总结

你已经学会:

  1. ✓ React事件命名规则(驼峰,如onClick)
  2. ✓ 事件绑定语法(onClick={handler})
  3. ✓ 常见事件:点击、输入、提交、键盘、鼠标
  4. ✓ 给事件处理函数传参
  5. preventDefault()stopPropagation()的使用
  6. ✓ 表单受控组件

React事件快速参考:

// 点击
<button onClick={handleClick}>点击</button>

// 输入
<input onChange={e => setText(e.target.value)} />

// 提交
<form onSubmit={handleSubmit}>...</form>

// 键盘
<input onKeyDown={handleKeyDown} />

// 鼠标
<div onMouseEnter={() => ...} onMouseLeave={() => ...}>

验收标准:

  • 能给按钮绑定点击事件
  • 能获取输入框的值
  • 能处理表单提交并阻止默认行为
  • 能给事件处理函数传递参数

下一步: 现在你已经掌握了交互的核心技术!下一阶段我们将学习条件渲染和列表渲染,这样就能显示动态数据了(比如显示一个Todo列表)!


💡 练习题

做一个 CommentBox 组件:

  • 输入框输入评论
  • 点击"发布"按钮,在下方显示评论
  • 点击"删除"可以删除评论
  • 提示:用数组State存储评论列表