React Events - Complete Guide
Events make React applications interactive. Understanding how to handle events properly is essential for building user interfaces that respond to user actions. This guide covers everything you need to know about React events.
What are React Events?
React events are actions that happen in the browser, like clicks, key presses, form submissions, and mouse movements. React wraps native browser events in a synthetic event system that provides consistent behavior across all browsers.
Basic Event Handling
Event Handler Syntax
React events are named using camelCase and passed as functions rather than strings:
// HTML (old way)
<button onclick="handleClick()">Click me</button>
// React (new way)
<button onClick={handleClick}>Click me</button>Simple Click Event
function ClickButton() {
const handleClick = () => {
console.log('Button clicked!');
alert('Hello from React!');
};
return <button onClick={handleClick}>Click me</button>;
}Event Handler with Parameters
function UserButtons() {
const handleUserClick = (userName) => {
alert(`Hello, ${userName}!`);
};
return (
<div>
<button onClick={() => handleUserClick('Alice')}>Greet Alice</button>
<button onClick={() => handleUserClick('Bob')}>Greet Bob</button>
</div>
);
}Common Event Types
Mouse Events
function MouseEvents() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [isClicked, setIsClicked] = useState(false);
const handleMouseMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
};
const handleMouseDown = () => setIsClicked(true);
const handleMouseUp = () => setIsClicked(false);
const handleMouseEnter = () => console.log('Mouse entered');
const handleMouseLeave = () => console.log('Mouse left');
return (
<div
className="mouse-area"
onMouseMove={handleMouseMove}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{
width: '300px',
height: '200px',
border: '2px solid #ccc',
backgroundColor: isClicked ? '#e0e0e0' : '#f0f0f0',
cursor: 'pointer'
}}
>
<p>Mouse position: {position.x}, {position.y}</p>
<p>Mouse is {isClicked ? 'pressed' : 'not pressed'}</p>
</div>
);
}Keyboard Events
function KeyboardEvents() {
const [keys, setKeys] = useState([]);
const [lastKey, setLastKey] = useState('');
const handleKeyDown = (e) => {
setLastKey(e.key);
if (!keys.includes(e.key)) {
setKeys([...keys, e.key]);
}
};
const handleKeyUp = (e) => {
setKeys(keys.filter(key => key !== e.key));
};
const handleKeyPress = (e) => {
console.log('Key pressed:', e.key);
};
return (
<div>
<input
type="text"
placeholder="Type here..."
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onKeyPress={handleKeyPress}
style={{ width: '300px', padding: '10px' }}
/>
<p>Last key: {lastKey}</p>
<p>Currently pressed: {keys.join(', ') || 'None'}</p>
</div>
);
}Form Events
function FormEvents() {
const [formData, setFormData] = useState({
name: '',
email: '',
select: 'option1'
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (e) => {
e.preventDefault(); // Prevent default form submission
console.log('Form submitted:', formData);
alert(`Form submitted with: ${JSON.stringify(formData)}`);
};
const handleReset = () => {
setFormData({
name: '',
email: '',
select: 'option1'
});
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name:</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
</div>
<div>
<label>Email:</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
</div>
<div>
<label>Select:</label>
<select
name="select"
value={formData.select}
onChange={handleChange}
>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
</div>
<button type="submit">Submit</button>
<button type="button" onClick={handleReset}>Reset</button>
</form>
);
}Focus Events
function FocusEvents() {
const [isFocused, setIsFocused] = useState(false);
const [focusCount, setFocusCount] = useState(0);
const handleFocus = () => {
setIsFocused(true);
setFocusCount(focusCount + 1);
};
const handleBlur = () => {
setIsFocused(false);
};
return (
<div>
<input
type="text"
placeholder="Click to focus"
onFocus={handleFocus}
onBlur={handleBlur}
style={{
border: isFocused ? '2px solid blue' : '2px solid gray',
padding: '10px'
}}
/>
<p>Input is {isFocused ? 'focused' : 'not focused'}</p>
<p>Focus count: {focusCount}</p>
</div>
);
}Event Object Properties
The event object contains useful information about the event:
function EventInfo() {
const handleClick = (e) => {
console.log('Event object:', e);
console.log('Target:', e.target);
console.log('Current target:', e.currentTarget);
console.log('Type:', e.type);
console.log('Time stamp:', e.timeStamp);
};
const handleInputChange = (e) => {
console.log('Input value:', e.target.value);
console.log('Input name:', e.target.name);
console.log('Input type:', e.target.type);
};
return (
<div>
<button onClick={handleClick}>Log Event Info</button>
<input
type="text"
placeholder="Type to see input events"
onChange={handleInputChange}
style={{ marginLeft: '10px', padding: '5px' }}
/>
</div>
);
}Event Propagation
Bubbling and Capturing
Events in React propagate in two phases: capturing (top-down) and bubbling (bottom-up):
function EventPropagation() {
const handleDivClick = (e) => {
console.log('Div clicked - Current target:', e.currentTarget);
};
const handleButtonClick = (e) => {
console.log('Button clicked - Current target:', e.currentTarget);
console.log('Button clicked - Target:', e.target);
// Stop propagation
// e.stopPropagation();
};
return (
<div onClick={handleDivClick} style={{ padding: '20px', backgroundColor: '#f0f0f0' }}>
<p>Click the button to see event propagation</p>
<button onClick={handleButtonClick}>Click me</button>
</div>
);
}Preventing Default Behavior
Some events have default browser behavior that you might want to prevent:
function PreventDefault() {
const handleSubmit = (e) => {
e.preventDefault(); // Prevent form from submitting/reloading page
console.log('Form submission prevented');
};
const handleLinkClick = (e) => {
e.preventDefault(); // Prevent link from navigating
console.log('Link navigation prevented');
};
const handleContextMenu = (e) => {
e.preventDefault(); // Prevent context menu
console.log('Context menu prevented');
};
return (
<div onContextMenu={handleContextMenu} style={{ padding: '20px' }}>
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Type and press Enter" />
<button type="submit">Submit</button>
</form>
<a href="https://example.com" onClick={handleLinkClick}>
This link won't navigate
</a>
<p>Right-click here to see context menu prevention</p>
</div>
);
}Event Handling Best Practices
1. Use Arrow Functions for Class Components
If you’re using class components, bind event handlers properly:
class ButtonComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// Method 1: Bind in constructor
this.handleClick = this.handleClick.bind(this);
}
// Method 2: Use arrow function as class property
handleIncrement = () => {
this.setState(prevState => ({ count: prevState.count + 1 }));
};
// Method 3: Regular method (needs binding)
handleClick() {
console.log('Button clicked');
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click me</button>
<button onClick={this.handleIncrement}>
Count: {this.state.count}
</button>
{/* Method 4: Inline arrow function */}
<button onClick={() => console.log('Inline click')}>
Inline
</button>
</div>
);
}
}2. Performance Considerations
Be careful with inline functions in lists:
function TodoList({ todos, onToggle }) {
// Bad - creates new function on every render
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<button onClick={() => onToggle(todo.id)}>
Toggle
</button>
{todo.text}
</li>
))}
</ul>
);
}
// Better - use data attributes or useCallback
function TodoList({ todos, onToggle }) {
const handleToggle = useCallback((e) => {
const todoId = parseInt(e.currentTarget.dataset.todoId);
onToggle(todoId);
}, [onToggle]);
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<button
data-todo-id={todo.id}
onClick={handleToggle}
>
Toggle
</button>
{todo.text}
</li>
))}
</ul>
);
}3. Clean Up Event Listeners
When adding event listeners to the DOM, remember to clean them up:
function WindowEvents() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
// Cleanup function
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div>
<p>Window size: {windowSize.width} x {windowSize.height}</p>
</div>
);
}Custom Event Handlers
Creating Reusable Event Handlers
function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = useState(false);
useEffect(() => {
const handleKeyDown = (e) => {
if (e.key === targetKey) {
setKeyPressed(true);
}
};
const handleKeyUp = (e) => {
if (e.key === targetKey) {
setKeyPressed(false);
}
};
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
return () => {
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
};
}, [targetKey]);
return keyPressed;
}
function KeyboardShortcuts() {
const enterPressed = useKeyPress('Enter');
const escapePressed = useKeyPress('Escape');
useEffect(() => {
if (enterPressed) {
console.log('Enter key pressed!');
}
}, [enterPressed]);
useEffect(() => {
if (escapePressed) {
console.log('Escape key pressed!');
}
}, [escapePressed]);
return (
<div>
<p>Press Enter or Escape to see console output</p>
<p>Enter pressed: {enterPressed ? 'Yes' : 'No'}</p>
<p>Escape pressed: {escapePressed ? 'Yes' : 'No'}</p>
</div>
);
}Common Event Patterns
1. Toggle Pattern
function ToggleExample() {
const [isVisible, setIsVisible] = useState(false);
const toggle = () => setIsVisible(!isVisible);
return (
<div>
<button onClick={toggle}>
{isVisible ? 'Hide' : 'Show'} Content
</button>
{isVisible && <p>This content can be toggled!</p>}
</div>
);
}2. Counter Pattern
function CounterExample() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(0);
return (
<div>
<h2>Count: {count}</h2>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}3. Search Pattern
function SearchExample() {
const [searchTerm, setSearchTerm] = useState('');
const [results, setResults] = useState([]);
const data = ['Apple', 'Banana', 'Orange', 'Grape', 'Strawberry'];
const handleSearch = (e) => {
const term = e.target.value.toLowerCase();
setSearchTerm(term);
const filtered = data.filter(item =>
item.toLowerCase().includes(term)
);
setResults(filtered);
};
return (
<div>
<input
type="text"
placeholder="Search fruits..."
value={searchTerm}
onChange={handleSearch}
/>
<ul>
{results.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}Accessibility Considerations
Keyboard Navigation
Make sure your interactive elements work with keyboard:
function AccessibleButton() {
const [isActive, setIsActive] = useState(false);
const handleClick = () => {
setIsActive(!isActive);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick();
}
};
return (
<button
onClick={handleClick}
onKeyDown={handleKeyDown}
aria-pressed={isActive}
style={{
backgroundColor: isActive ? 'blue' : 'gray',
color: 'white',
padding: '10px 20px',
border: 'none',
cursor: 'pointer'
}}
>
{isActive ? 'Active' : 'Inactive'}
</button>
);
}Summary
React events are fundamental to building interactive applications. Remember these key points:
- Use camelCase for event names (onClick, onChange, etc.)
- Pass event handlers as functions, not strings
- Use the event object to get information about the event
- Prevent default behavior when needed with
e.preventDefault() - Stop event propagation with
e.stopPropagation() - Clean up event listeners to prevent memory leaks
- Consider performance when using inline functions
- Make your components accessible with proper keyboard handling
Mastering React events will help you create rich, interactive user experiences that work seamlessly across all browsers.
External Resources:
- React Official Docs - Handling Events
- MDN Web Docs - Event Reference
- React SyntheticEvent Documentation
Related Tutorials: