Synthetic Events in React
Synthetic events are React’s cross-browser wrapper around the browser’s native events. They provide a consistent API regardless of the browser being used.
Table of Contents
What are Synthetic Events?
Synthetic events are React’s wrapper around native DOM events. They provide a consistent interface across different browsers and normalize event behavior.
Key Characteristics:
- Cross-browser: Work consistently across all browsers
- Normalized: Same API regardless of browser differences
- Pooled: Events are reused for performance
- Synthetic: Not actual DOM events, but React’s abstraction
Event Object Structure:
// Synthetic event object
const syntheticEvent = {
type: 'click',
target: DOMElement,
currentTarget: DOMElement,
preventDefault: function() { /* ... */ },
stopPropagation: function() { /* ... */ },
nativeEvent: DOMEvent, // Access to original DOM event
// ... other normalized properties
};
Event Handling
Basic Event Handling:
function Button() {
const handleClick = (event) => {
console.log('Button clicked!');
console.log('Event type:', event.type);
console.log('Target:', event.target);
console.log('Native event:', event.nativeEvent);
};
return (
<button onClick={handleClick}>
Click Me
</button>
);
}
Event Prevention:
function Form() {
const handleSubmit = (event) => {
event.preventDefault(); // Prevent form submission
event.stopPropagation(); // Stop event bubbling
console.log('Form submitted');
};
return (
<form onSubmit={handleSubmit}>
<input type="text" />
<button type="submit">Submit</button>
</form>
);
}
Event Parameters:
function InputField() {
const handleChange = (event) => {
const value = event.target.value;
console.log('Input value:', value);
};
const handleKeyPress = (event) => {
if (event.key === 'Enter') {
console.log('Enter pressed');
}
};
return (
<input
onChange={handleChange}
onKeyPress={handleKeyPress}
placeholder="Type something..."
/>
);
}
Multiple Event Handlers:
function InteractiveElement() {
const handleMouseEnter = (event) => {
console.log('Mouse entered');
};
const handleMouseLeave = (event) => {
console.log('Mouse left');
};
const handleClick = (event) => {
console.log('Clicked');
};
return (
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onClick={handleClick}
style={{ padding: '20px', border: '1px solid black' }}
>
Hover and click me
</div>
);
}
Event Pooling
What is Event Pooling:
React reuses event objects for performance. After an event handler finishes, the event object is returned to the pool and its properties are nullified.
Event Pooling Example:
function EventPoolingExample() {
const handleClick = (event) => {
// Good: Access event properties immediately
console.log('Event type:', event.type);
console.log('Target:', event.target);
// Bad: Accessing event properties asynchronously
setTimeout(() => {
console.log('Event type:', event.type); // null
console.log('Target:', event.target); // null
}, 100);
};
return (
<button onClick={handleClick}>
Click Me
</button>
);
}
Handling Event Pooling:
function EventPoolingSolution() {
const handleClick = (event) => {
// Good: Store values immediately
const eventType = event.type;
const target = event.target;
setTimeout(() => {
console.log('Event type:', eventType); // Works
console.log('Target:', target); // Works
}, 100);
};
return (
<button onClick={handleClick}>
Click Me
</button>
);
}
Using event.persist():
function PersistExample() {
const handleClick = (event) => {
// Good: Persist the event for async use
event.persist();
setTimeout(() => {
console.log('Event type:', event.type); // Works
console.log('Target:', event.target); // Works
}, 100);
};
return (
<button onClick={handleClick}>
Click Me
</button>
);
}
Best Practices
1. Access Event Properties Immediately:
// Good: Access properties immediately
function GoodHandler(event) {
const value = event.target.value;
const type = event.type;
// Use values immediately or store them
console.log(value, type);
}
// Bad: Access properties asynchronously
function BadHandler(event) {
setTimeout(() => {
console.log(event.target.value); // null
}, 100);
}
2. Use event.persist() for Async Operations:
// Good: Persist event for async use
function AsyncHandler(event) {
event.persist();
fetch('/api/data')
.then(() => {
console.log('Event type:', event.type); // Works
});
}
// Good: Store values immediately
function BetterAsyncHandler(event) {
const eventData = {
type: event.type,
target: event.target,
value: event.target.value
};
fetch('/api/data', {
method: 'POST',
body: JSON.stringify(eventData)
});
}
3. Prevent Default Behavior:
// Good: Prevent default when needed
function FormHandler(event) {
event.preventDefault();
// Custom form handling
const formData = new FormData(event.target);
console.log('Form data:', formData);
}
// Good: Stop propagation when needed
function NestedHandler(event) {
event.stopPropagation();
// Handle event without bubbling to parent
console.log('Handled locally');
}
4. Use Proper Event Types:
// Good: Use appropriate event handlers
function InputComponent() {
const handleChange = (event) => {
console.log('Value changed:', event.target.value);
};
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
console.log('Enter pressed');
}
};
const handleFocus = (event) => {
console.log('Input focused');
};
return (
<input
onChange={handleChange}
onKeyDown={handleKeyDown}
onFocus={handleFocus}
/>
);
}
5. Handle Multiple Events Efficiently:
// Good: Single handler for multiple events
function EfficientHandler() {
const handleInteraction = (event) => {
switch (event.type) {
case 'click':
console.log('Clicked');
break;
case 'mouseenter':
console.log('Mouse entered');
break;
case 'mouseleave':
console.log('Mouse left');
break;
default:
break;
}
};
return (
<div
onClick={handleInteraction}
onMouseEnter={handleInteraction}
onMouseLeave={handleInteraction}
>
Interactive element
</div>
);
}
Common Interview Questions
Q: What are synthetic events in React?
- React’s cross-browser wrapper around native DOM events that provides a consistent API.
Q: Why does React use synthetic events?
- To ensure consistent behavior across different browsers and normalize event handling.
Q: What is event pooling in React?
- React reuses event objects for performance, nullifying their properties after the event handler finishes.
Q: How do you handle events in async operations?
- Use event.persist() or store event properties immediately before async operations.
Q: What’s the difference between event.target and event.currentTarget?
- event.target is the element that triggered the event, while event.currentTarget is the element that the event handler is attached to.
Q: How do you prevent default behavior in React?
- Use event.preventDefault() to prevent the default browser behavior.
Q: How do you stop event propagation?
- Use event.stopPropagation() to prevent the event from bubbling up to parent elements.
Q: Can you access the native DOM event?
- Yes, through event.nativeEvent property.
Summary
- Synthetic events are React’s cross-browser wrapper around native DOM events
- Event pooling reuses event objects for performance optimization
- Access event properties immediately or use event.persist() for async operations
- preventDefault() and stopPropagation() control event behavior
- event.target vs event.currentTarget have different meanings
- event.nativeEvent provides access to the original DOM event
- Synthetic events ensure consistent behavior across all browsers
- Proper event handling is crucial for React performance and functionality
Interview angle
- “What is a synthetic event?” - React’s cross-browser wrapper over the native event, with a consistent API. Since React 17 listeners are attached at the root container rather than
document, which is what makes multiple React roots and React-inside-non-React apps behave. - “Do you still need
e.persist()?” - no. Event pooling was removed in React 17, so the event object survives into async callbacks. Advice telling you to callpersist()predates that. - “How does bubbling work through portals?” - along the React tree, not the DOM tree. A click inside a portalled modal reaches handlers on its React ancestors even though the DOM node is elsewhere.
- “When do you reach for a native listener?” - for events React does not synthesise or that need non-passive
preventDefaulton scroll and touch, and for listeners onwindowordocument. Add them in an effect and remove them in the cleanup.