import React, { useState } from 'react';
function ClickCounter() {
const [count, setCount] = useState(0);
// 事件处理函数
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>点击次数: {count}</p>
{/* 注意:onClick 驼峰命名 */}
<button onClick={handleClick}>
点击我
</button>
</div>
);
}
export default ClickCounter;
点击次数: 0
function InlineEventHandler() {
const [message, setMessage] = useState('');
return (
<div>
<p>{message}</p>
{/* 内联箭头函数 */}
<button onClick={() => setMessage('按钮被点击了!')}>
显示消息
</button>
{/* 内联普通函数 */}
<button onClick={function() {
console.log('按钮被点击了');
alert('按钮点击事件触发');
}}>
显示警告
</button>
</div>
);
}
React中的事件对象是合成事件(SyntheticEvent),是跨浏览器包装器,与原生事件接口相同。
function EventObjectDemo() {
const handleClick = (event) => {
// 阻止默认行为
event.preventDefault();
// 停止事件冒泡
event.stopPropagation();
// 访问事件目标
console.log('事件目标:', event.target);
console.log('当前目标:', event.currentTarget);
console.log('事件类型:', event.type);
// 鼠标事件特有属性
console.log('鼠标位置:', event.clientX, event.clientY);
};
const handleChange = (event) => {
// 输入事件
console.log('输入的值:', event.target.value);
};
const handleKeyDown = (event) => {
// 键盘事件
console.log('按下的键:', event.key);
console.log('键码:', event.keyCode);
// 按下回车键
if (event.key === 'Enter') {
console.log('用户按下了回车键');
}
};
return (
<div onClick={handleClick}>
<a href="https://reactjs.org" onClick={handleClick}>
点击我(不会跳转)
</a>
<br />
<input
type="text"
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder="输入并按回车"
/>
</div>
);
}
| 事件类型 | React属性 | 描述 | 示例 |
|---|---|---|---|
| 点击事件 | onClick |
元素被点击时触发 | <button onClick={handleClick}> |
| 变化事件 | onChange |
表单元素值变化时触发 | <input onChange={handleChange}> |
| 提交事件 | onSubmit |
表单提交时触发 | <form onSubmit={handleSubmit}> |
| 鼠标事件 | onMouseEnter, onMouseLeave |
鼠标进入/离开元素 | <div onMouseEnter={handleMouseEnter}> |
| 键盘事件 | onKeyDown, onKeyUp |
键盘按键按下/释放 | <input onKeyDown={handleKeyDown}> |
| 焦点事件 | onFocus, onBlur |
元素获得/失去焦点 | <input onFocus={handleFocus}> |
function ItemList() {
const items = ['苹果', '香蕉', '橙子', '葡萄'];
const handleClick = (item, index, event) => {
console.log(`你点击了: ${item}, 索引: ${index}`);
console.log('事件对象:', event);
};
return (
<ul>
{items.map((item, index) => (
<li key={index}>
{/* 使用箭头函数传递参数 */}
<button onClick={(e) => handleClick(item, index, e)}>
选择 {item}
</button>
</li>
))}
</ul>
);
}
class ItemListClass extends React.Component {
handleClick(item, index, event) {
console.log(`你点击了: ${item}, 索引: ${index}`);
console.log('事件对象:', event);
}
render() {
const items = ['苹果', '香蕉', '橙子', '葡萄'];
return (
<ul>
{items.map((item, index) => (
<li key={index}>
{/* 使用bind传递参数 */}
<button onClick={this.handleClick.bind(this, item, index)}>
选择 {item}
</button>
</li>
))}
</ul>
);
}
}
function DataAttributeDemo() {
const handleClick = (event) => {
const itemId = event.target.dataset.id;
const itemName = event.target.dataset.name;
console.log(`ID: ${itemId}, 名称: ${itemName}`);
};
const items = [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' }
];
return (
<div>
{items.map(item => (
<button
key={item.id}
data-id={item.id}
data-name={item.name}
onClick={handleClick}
className="event-button"
>
{item.name}
</button>
))}
</div>
);
}
function FormExample() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: '',
rememberMe: false,
gender: 'male',
country: 'china'
});
// 处理输入变化
const handleInputChange = (event) => {
const { name, value, type, checked } = event.target;
setFormData(prevState => ({
...prevState,
[name]: type === 'checkbox' ? checked : value
}));
};
// 处理表单提交
const handleSubmit = (event) => {
event.preventDefault();
console.log('表单数据:', formData);
// 发送到服务器...
alert('表单提交成功!');
};
// 重置表单
const handleReset = () => {
setFormData({
username: '',
email: '',
password: '',
rememberMe: false,
gender: 'male',
country: 'china'
});
};
return (
<form onSubmit={handleSubmit} className="form-demo">
<div className="mb-3">
<label>用户名:</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleInputChange}
placeholder="请输入用户名"
/>
</div>
<div className="mb-3">
<label>邮箱:</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleInputChange}
placeholder="example@email.com"
/>
</div>
<div className="mb-3">
<label>密码:</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleInputChange}
/>
</div>
<div className="mb-3">
<label>
<input
type="checkbox"
name="rememberMe"
checked={formData.rememberMe}
onChange={handleInputChange}
/>
记住我
</label>
</div>
<div className="mb-3">
<label>性别:</label>
<label>
<input
type="radio"
name="gender"
value="male"
checked={formData.gender === 'male'}
onChange={handleInputChange}
/>
男
</label>
<label>
<input
type="radio"
name="gender"
value="female"
checked={formData.gender === 'female'}
onChange={handleInputChange}
/>
女
</label>
</div>
<div className="mb-3">
<label>国家:</label>
<select
name="country"
value={formData.country}
onChange={handleInputChange}
>
<option value="china">中国</option>
<option value="usa">美国</option>
<option value="japan">日本</option>
<option value="uk">英国</option>
</select>
</div>
<button type="submit">提交</button>
<button type="button" onClick={handleReset}>重置</button>
</form>
);
}
React的事件系统使用合成事件,它是跨浏览器的事件包装器,具有与原生事件相同的接口。
function SyntheticEventDemo() {
const handleEvent = (e) => {
// 合成事件属性
console.log('合成事件类型:', e.type);
console.log('原生事件:', e.nativeEvent);
// 合成事件会被重用,不能异步访问
// 错误做法:
// setTimeout(() => {
// console.log(e.type); // 可能为null
// }, 100);
// 正确做法:持久化事件
e.persist();
setTimeout(() => {
console.log('异步访问事件类型:', e.type);
}, 100);
};
return (
<button onClick={handleEvent}>
测试合成事件
</button>
);
}
event.persist()。
function EventBubblingDemo() {
const handleParentClick = () => {
console.log('父元素被点击');
};
const handleChildClick = (e) => {
console.log('子元素被点击');
// 阻止事件冒泡
e.stopPropagation();
};
const handleGrandchildClick = (e) => {
console.log('孙元素被点击');
};
return (
<div
className="parent"
onClick={handleParentClick}
style={{ padding: '20px', background: '#f0f0f0' }}
>
父元素
<div
className="child"
onClick={handleChildClick}
style={{ padding: '20px', background: '#e0e0e0', margin: '10px' }}
>
子元素
<div
className="grandchild"
onClick={handleGrandchildClick}
style={{ padding: '20px', background: '#d0d0d0', margin: '10px' }}
>
孙元素
</div>
</div>
</div>
);
}
function EventCaptureDemo() {
const handleCapture = () => {
console.log('捕获阶段: 父元素');
};
const handleBubble = () => {
console.log('冒泡阶段: 父元素');
};
const handleChildClick = () => {
console.log('子元素被点击');
};
return (
<div
onClickCapture={handleCapture} // 捕获阶段
onClick={handleBubble} // 冒泡阶段
style={{ padding: '20px', background: '#f0f0f0' }}
>
父元素(同时处理捕获和冒泡)
<button onClick={handleChildClick}>
点击我
</button>
</div>
);
}
// 简单的事件总线实现
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
off(event, callback) {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter(
cb => cb !== callback
);
}
emit(event, data) {
if (!this.events[event]) return;
this.events[event].forEach(callback => {
callback(data);
});
}
}
// 创建全局事件总线实例
const eventBus = new EventBus();
// 组件A:发布事件
function ComponentA() {
const handleClick = () => {
eventBus.emit('customEvent', {
message: '来自ComponentA的消息',
timestamp: new Date().toISOString()
});
};
return <button onClick={handleClick}>发送事件</button>;
}
// 组件B:订阅事件
function ComponentB() {
const [message, setMessage] = useState('');
useEffect(() => {
const handleCustomEvent = (data) => {
setMessage(data.message);
};
eventBus.on('customEvent', handleCustomEvent);
// 清理订阅
return () => {
eventBus.off('customEvent', handleCustomEvent);
};
}, []);
return <div>接收到的消息: {message}</div>;
}
import React, { useCallback, memo } from 'react';
// 1. 使用useCallback缓存事件处理器
function OptimizedComponent() {
const [count, setCount] = useState(0);
// 使用useCallback避免每次渲染都创建新函数
const handleClick = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // 依赖数组为空,函数只创建一次
return (
<div>
<p>计数: {count}</p>
<MemoizedButton onClick={handleClick} />
</div>
);
}
// 2. 使用memo避免不必要的重新渲染
const MemoizedButton = memo(function MemoizedButton({ onClick }) {
console.log('MemoizedButton 渲染');
return <button onClick={onClick}>点击我</button>;
});
// 3. 事件委托(适合大量子元素)
function EventDelegationDemo() {
const handleListClick = useCallback((event) => {
// 使用事件委托
if (event.target.tagName === 'BUTTON') {
const itemId = event.target.dataset.id;
console.log(`点击了项目 ${itemId}`);
}
}, []);
const items = Array.from({ length: 100 }, (_, i) => ({
id: i + 1,
name: `项目 ${i + 1}`
}));
return (
<div onClick={handleListClick}>
{items.map(item => (
<div key={item.id} className="item">
<span>{item.name}</span>
<button data-id={item.id}>选择</button>
</div>
))}
</div>
);
}
useCallback缓存事件处理器memouseEffect的返回函数中)event.persist()异步访问合成事件// 错误1:忘记调用事件处理器
function WrongExample1() {
const handleClick = () => {
console.log('被点击了');
};
return (
<div>
{/* 错误:handleClick 后面缺少 () 实际上不会执行 */}
<button onClick={handleClick}>正确</button>
{/* 错误:这样会立即执行,而不是点击时执行 */}
<button onClick={handleClick()}>错误</button>
</div>
);
}
// 错误2:异步访问合成事件
function WrongExample2() {
const handleClick = (e) => {
// 错误:异步访问事件属性
setTimeout(() => {
console.log(e.type); // 可能为null或undefined
}, 100);
// 正确:持久化事件
// e.persist();
// setTimeout(() => {
// console.log(e.type);
// }, 100);
};
return <button onClick={handleClick}>测试</button>;
}
// 错误3:在类组件中忘记绑定this
class WrongExample3 extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// 需要在构造函数中绑定this
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 如果没有绑定this,这里this会是undefined
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<p>计数: {this.state.count}</p>
{/* 或者使用箭头函数 */}
<button onClick={() => this.handleClick()}>
点击我
</button>
</div>
);
}
}