React Event Handling
Overview
React provides a powerful event handling system that allows us to respond to various user interactions. This chapter will learn how React events work, how to write event handlers, how to use event objects, and various common user interaction scenarios.
🎯 React Event System
SyntheticEvent
jsx
function EventBasics() {
const handleClick = (event) => {
console.log('Event type:', event.type);
console.log('Target element:', event.target);
console.log('Current element:', event.currentTarget);
// Prevent default behavior
event.preventDefault();
// Stop event propagation
event.stopPropagation();
};
return (
<div>
<button onClick={handleClick}>Click Me</button>
<a href="#" onClick={handleClick}>
Click Link (Default Behavior Prevented)
</a>
</div>
);
}Event Binding Methods
jsx
// Function component recommended approach
function ModernEventBinding() {
const [count, setCount] = React.useState(0);
// Recommended: Use useCallback for performance optimization
const handleClick = React.useCallback(() => {
setCount(prev => prev + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
// Class component event binding
class ClassEventBinding extends React.Component {
state = { count: 0 };
// Recommended: Arrow function property
handleClick = () => {
this.setState(prev => ({ count: prev.count + 1 }));
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>Increment</button>
</div>
);
}
}🖱️ Mouse Events
Basic Mouse Events
jsx
function MouseEvents() {
const [position, setPosition] = React.useState({ x: 0, y: 0 });
const [clicked, setClicked] = React.useState(false);
const handleMouseMove = (event) => {
setPosition({ x: event.clientX, y: event.clientY });
};
const handleClick = () => {
setClicked(true);
setTimeout(() => setClicked(false), 1000);
};
return (
<div
onMouseMove={handleMouseMove}
style={{ height: '200px', border: '1px solid #ccc', padding: '20px' }}
>
<p>Mouse position: ({position.x}, {position.y})</p>
<button
onClick={handleClick}
style={{ backgroundColor: clicked ? 'green' : 'blue', color: 'white' }}
>
{clicked ? 'Clicked!' : 'Click Me'}
</button>
</div>
);
}Drag and Drop
jsx
function DragAndDrop() {
const [items, setItems] = React.useState([
{ id: 1, text: 'Drag me', x: 0, y: 0 }
]);
const [dragging, setDragging] = React.useState(null);
const handleMouseDown = (event, item) => {
setDragging({
id: item.id,
offsetX: event.clientX - item.x,
offsetY: event.clientY - item.y
});
};
const handleMouseMove = (event) => {
if (dragging) {
setItems(items.map(item =>
item.id === dragging.id
? {
...item,
x: event.clientX - dragging.offsetX,
y: event.clientY - dragging.offsetY
}
: item
));
}
};
const handleMouseUp = () => {
setDragging(null);
};
React.useEffect(() => {
if (dragging) {
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
}
}, [dragging]);
return (
<div style={{ position: 'relative', height: '300px', border: '1px solid #ccc' }}>
{items.map(item => (
<div
key={item.id}
onMouseDown={(e) => handleMouseDown(e, item)}
style={{
position: 'absolute',
left: item.x,
top: item.y,
padding: '10px',
backgroundColor: 'lightblue',
cursor: 'move',
userSelect: 'none'
}}
>
{item.text}
</div>
))}
</div>
);
}⌨️ Keyboard Events
Keyboard Input Handling
jsx
function KeyboardEvents() {
const [input, setInput] = React.useState('');
const [history, setHistory] = React.useState([]);
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
if (input.trim()) {
setHistory([...history, input]);
setInput('');
}
} else if (event.key === 'Escape') {
setInput('');
}
};
return (
<div>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Enter content, press Enter to submit, Escape to clear"
style={{ width: '300px', padding: '10px' }}
/>
<h3>History:</h3>
<ul>
{history.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}📝 Form Events
Form Control Events
jsx
function FormEvents() {
const [formData, setFormData] = React.useState({
name: '',
email: '',
gender: '',
agree: false
});
const handleChange = (event) => {
const { name, value, type, checked } = event.target;
setFormData({
...formData,
[name]: type === 'checkbox' ? checked : value
});
};
const handleSubmit = (event) => {
event.preventDefault();
console.log('Form data:', formData);
alert('Form submitted successfully!');
};
return (
<form onSubmit={handleSubmit} style={{ maxWidth: '400px' }}>
<div style={{ marginBottom: '15px' }}>
<label>Name:</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
style={{ width: '100%', padding: '8px' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label>Email:</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
style={{ width: '100%', padding: '8px' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label>Gender:</label>
<select name="gender" value={formData.gender} onChange={handleChange}>
<option value="">Please select</option>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</div>
<div style={{ marginBottom: '15px' }}>
<label>
<input
type="checkbox"
name="agree"
checked={formData.agree}
onChange={handleChange}
/>
I agree to the terms
</label>
</div>
<button type="submit" disabled={!formData.agree}>
Submit
</button>
</form>
);
}⚡ Event Performance Optimization
Debounce and Throttle
jsx
function EventOptimization() {
const [searchTerm, setSearchTerm] = React.useState('');
const [debouncedTerm, setDebouncedTerm] = React.useState('');
// Debounce processing
React.useEffect(() => {
const timer = setTimeout(() => {
setDebouncedTerm(searchTerm);
}, 300);
return () => clearTimeout(timer);
}, [searchTerm]);
// Throttle processing
const [scrollPosition, setScrollPosition] = React.useState(0);
const throttledScroll = React.useCallback(
React.useMemo(() => {
let lastRun = 0;
return (event) => {
if (Date.now() - lastRun >= 100) {
setScrollPosition(event.target.scrollTop);
lastRun = Date.now();
}
};
}, []),
[]
);
return (
<div>
<h3>Search (Debounced)</h3>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Enter search content..."
/>
<p>Search term: {debouncedTerm}</p>
<h3>Scroll (Throttled)</h3>
<div
onScroll={throttledScroll}
style={{ height: '100px', overflow: 'auto', border: '1px solid #ccc' }}
>
<div style={{ height: '300px', padding: '10px' }}>
Scroll this area to view position info
<br />Scroll position: {scrollPosition}
</div>
</div>
</div>
);
}🎪 Event Delegation
Efficient Event Handling
jsx
function EventDelegation() {
const [items, setItems] = React.useState([
{ id: 1, name: 'Item 1', active: false },
{ id: 2, name: 'Item 2', active: false },
{ id: 3, name: 'Item 3', active: false }
]);
// Event delegation handling
const handleContainerClick = (event) => {
const button = event.target.closest('button');
if (button) {
const action = button.dataset.action;
const itemId = parseInt(button.dataset.itemId);
if (action === 'toggle') {
setItems(prev =>
prev.map(item =>
item.id === itemId ? { ...item, active: !item.active } : item
)
);
} else if (action === 'delete') {
setItems(prev => prev.filter(item => item.id !== itemId));
}
}
};
const addItem = () => {
const newId = Math.max(...items.map(item => item.id)) + 1;
setItems([...items, { id: newId, name: `Item ${newId}`, active: false }]);
};
return (
<div onClick={handleContainerClick}>
<button onClick={addItem} style={{ marginBottom: '10px' }}>
Add Item
</button>
{items.map(item => (
<div
key={item.id}
style={{
padding: '10px',
margin: '5px 0',
border: '1px solid #ddd',
backgroundColor: item.active ? '#e7f3ff' : '#f8f9fa'
}}
>
{item.name} {item.active ? '(Active)' : ''}
<button
data-action="toggle"
data-item-id={item.id}
style={{ marginLeft: '10px' }}
>
Toggle
</button>
<button
data-action="delete"
data-item-id={item.id}
style={{ marginLeft: '5px', color: 'red' }}
>
Delete
</button>
</div>
))}
</div>
);
}📝 Chapter Summary
Through this chapter, you should have mastered:
Core Concepts
- ✅ How React synthetic events work
- ✅ Properties and methods of event objects
- ✅ Best practices for event binding
- ✅ Event bubbling and default behavior control
Event Types
- ✅ Mouse events: click, move, drag
- ✅ Keyboard events: key handling, shortcuts
- ✅ Form events: input, submit, validation
- ✅ Touch events: mobile interactions
Performance Optimization
- ✅ Event delegation to reduce listener count
- ✅ Debounce and throttle for frequent events
- ✅ useCallback to cache event handlers
- ✅ Proper event binding timing
Best Practices
- Use synthetic events: Good compatibility, complete functionality
- Avoid inline functions: Use useCallback for optimization
- Use event delegation reasonably: Reduce memory usage
- Handle cleanup logic: Prevent memory leaks
- Monitor performance: Pay attention to event handling performance
Continue Learning: Next Chapter - React Conditional and List Rendering