第七章:事件处理
7.1 React 事件系统
React 实现了一个合成事件系统(SyntheticEvent),它是浏览器原生事件的跨浏览器包装器。
与原生事件的区别:
- 命名使用 camelCase:onClick 而非 onclick
- 使用 JSX 表达式传入函数:onClick={handleClick} 而非 onClick=“handleClick()”
- 不能通过 return false 阻止默认行为,必须显式调用 preventDefault
示例:
// HTML <button onclick="activateLasers()">激活激光</button> // React <button onClick={activateLasers}>激活激光</button>
阻止默认行为:
// HTML <a href="#" onclick="console.log('点击'); return false;">链接</a> // React function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('链接被点击'); } return ( <a href="https://example.com" onClick={handleClick}> 点击我 </a> ); }
7.2 类组件中的事件绑定
绑定 this 的方法:
class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; // 方法 1:在构造函数中绑定 this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } }
其他绑定方法:
class Toggle extends React.Component { state = { isToggleOn: true }; // 方法 2:使用类属性语法(推荐) handleClick = () => { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); }; // 方法 3:在 render 中使用箭头函数(不推荐,每次渲染都创建新函数) render() { return ( <button onClick={() => this.handleClick()}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } }
7.3 向事件处理函数传递参数
class List extends React.Component { handleClick = (id, event) => { console.log('ID:', id); console.log('Event:', event); }; render() { return ( <ul> {this.props.items.map(item => ( // 方法 1:箭头函数 <li key={item.id} onClick={(e) => this.handleClick(item.id, e)}> {item.name} </li> // 方法 2:bind(事件对象会自动传递) <li key={item.id} onClick={this.handleClick.bind(this, item.id)}> {item.name} </li> ))} </ul> ); } }
7.4 常用事件
鼠标事件:
function MouseEvents() { const handleClick = () => console.log('点击'); const handleDoubleClick = () => console.log('双击'); const handleMouseEnter = () => console.log('鼠标进入'); const handleMouseLeave = () => console.log('鼠标离开'); const handleMouseMove = (e) => console.log('移动:', e.clientX, e.clientY); const handleContextMenu = (e) => { e.preventDefault(); console.log('右键菜单'); }; return ( <div onClick={handleClick} onDoubleClick={handleDoubleClick} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} onMouseMove={handleMouseMove} onContextMenu={handleContextMenu} > 鼠标事件区域 </div> ); }
键盘事件:
function KeyboardEvents() { const handleKeyDown = (e) => { console.log('按下:', e.key, '键码:', e.keyCode); if (e.key === 'Enter') { console.log('回车键'); } if (e.ctrlKey && e.key === 's') { e.preventDefault(); console.log('Ctrl+S'); } }; const handleKeyUp = (e) => { console.log('释放:', e.key); }; return ( <input type="text" onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} /> ); }
表单事件:
function FormEvents() { const handleChange = (e) => { console.log('值变化:', e.target.value); }; const handleFocus = () => console.log('获得焦点'); const handleBlur = () => console.log('失去焦点'); const handleInput = (e) => console.log('输入:', e.target.value); return ( <input type="text" onChange={handleChange} onFocus={handleFocus} onBlur={handleBlur} onInput={handleInput} /> ); }
焦点事件:
function FocusEvents() { const handleFocus = (e) => { console.log('焦点在:', e.target.name); }; const handleBlur = (e) => { console.log('离开:', e.target.name); }; return ( <form> <input name="username" onFocus={handleFocus} onBlur={handleBlur} /> <input name="password" onFocus={handleFocus} onBlur={handleBlur} /> </form> ); }
7.5 事件对象
React 的合成事件对象(SyntheticEvent)是对原生事件的跨浏览器包装。
常用属性:
- boolean bubbles
- boolean cancelable
- DOMEventTarget currentTarget
- boolean defaultPrevented
- number eventPhase
- boolean isTrusted
- DOMEventTarget target
- number timeStamp
- string type
常用方法:
- preventDefault() - 阻止默认行为
- stopPropagation() - 阻止事件冒泡
- persist() - React 17+ 已不需要,事件对象现在是持久的
function EventDemo() { const handleClick = (e) => { console.log('事件类型:', e.type); console.log('目标元素:', e.target); console.log('当前元素:', e.currentTarget); console.log('时间戳:', e.timeStamp); e.preventDefault(); // 阻止默认行为 e.stopPropagation(); // 阻止冒泡 }; return <a href="/" onClick={handleClick}>点击</a>; }
7.6 事件委托
React 使用事件委托机制,所有事件都被委托到 document 或应用根容器(React 17+)。
优点:
- 减少内存消耗(不需要为每个元素绑定事件)
- 动态添加的元素自动有事件处理
注意:
- 事件处理函数中的 event.target 是实际触发事件的元素
- event.currentTarget 是绑定事件处理函数的元素
7.7 表单处理
受控组件:
function NameForm() { const [value, setValue] = useState(''); const handleChange = (e) => { setValue(e.target.value); }; const handleSubmit = (e) => { e.preventDefault(); alert('提交的名字: ' + value); }; return ( <form onSubmit={handleSubmit}> <label> 名字: <input type="text" value={value} onChange={handleChange} /> </label> <input type="submit" value="提交" /> </form> ); }
textarea:
// HTML 中 textarea 的值通过子元素设置 // React 中通过 value 属性设置 function EssayForm() { const [value, setValue] = useState('请撰写关于你喜欢的事物的文章'); return ( <textarea value={value} onChange={(e) => setValue(e.target.value)} /> ); }
select:
function FlavorForm() { const [value, setValue] = useState('coconut'); return ( <label> 选择你喜欢的风味: <select value={value} onChange={(e) => setValue(e.target.value)}> <option value="grapefruit">葡萄柚</option> <option value="lime">酸橙</option> <option value="coconut">椰子</option> <option value="mango">芒果</option> </select> </label> ); }
处理多个输入:
function ReservationForm() { const [state, setState] = useState({ isGoing: true, numberOfGuests: 2 }); const handleChange = (e) => { const { name, type, checked, value } = e.target; setState({ ...state, [name]: type === 'checkbox' ? checked : value }); }; return ( <form> <label> 参与: <input name="isGoing" type="checkbox" checked={state.isGoing} onChange={handleChange} /> </label> <br /> <label> 来宾人数: <input name="numberOfGuests" type="number" value={state.numberOfGuests} onChange={handleChange} /> </label> </form> ); }
非受控组件:
function NameForm() { const inputRef = useRef(null); const handleSubmit = (e) => { e.preventDefault(); alert('Name: ' + inputRef.current.value); }; return ( <form onSubmit={handleSubmit}> <input type="text" ref={inputRef} /> <button type="submit">提交</button> </form> ); }
7.8 事件性能优化
使用 useCallback 缓存事件处理函数:
import { useCallback } from 'react'; function Parent() { const [count, setCount] = useState(0); // 每次渲染都创建新函数 const handleClick1 = () => { setCount(c => c + 1); }; // 只在依赖变化时创建新函数 const handleClick2 = useCallback(() => { setCount(c => c + 1); }, []); return ( <div> <Child onClick={handleClick1} /> <Child onClick={handleClick2} /> </div> ); }
避免在 render 中创建新函数:
// 不推荐 function List({ items }) { return ( <ul> {items.map(item => ( <Item key={item.id} onClick={() => handleClick(item.id)} // 每次渲染都创建新函数 /> ))} </ul> ); } // 推荐 function List({ items }) { return ( <ul> {items.map(item => ( <ItemContainer key={item.id} item={item} /> ))} </ul> ); } function ItemContainer({ item }) { const handleClick = useCallback(() => { // 处理点击 }, [item.id]); return <Item onClick={handleClick} />; }
7.9 总结
本章详细介绍了 React 的事件处理:
- 合成事件系统
- 事件绑定和 this 处理
- 常用事件类型
- 表单处理(受控和非受控组件)
- 事件委托机制
- 事件性能优化
事件处理是 React 开发的基础,掌握这些知识对于构建交互式应用至关重要。