Synthetic Events are a cross-browser wrapper over native browser events provided by React. They ensure a consistent API for working with events across all browsers and add some useful features.
✅ Synthetic Events advantages:
❌ Features:
event.persist()
for asynchronous accessKey rule: Use Synthetic Events by default, switch to native events only when necessary! 🎯
Imagine you’re driving in different countries. Each country has different traffic rules and different steering wheels. Synthetic Events are like an international driver’s license that allows you to drive the same way everywhere! 🚗
Synthetic Events are React’s event system that provides a unified interface for working with events:
// Synthetic Event in action
function ButtonComponent() {
const handleClick = (event) => {
// event is a SyntheticEvent, not a native event!
console.log(event.target); // Works in all browsers
console.log(event.type); // Always correctly identifies event type
};
return <button onClick={handleClick}>Click me</button>;
}
Synthetic Events solve cross-browser compatibility issues:
// Without Synthetic Events - problems:
// In old IE: event.target vs event.srcElement
// In different browsers: different method names
// Different event objects
// With Synthetic Events - it's simple:
function CrossBrowserComponent() {
const handleChange = (event) => {
// Always event.target, regardless of browser
console.log(event.target.value);
};
return <input onChange={handleChange} />;
}
React uses event delegation at the root level:
// React automatically delegates events
function EventDelegation() {
const handleListClick = (event) => {
// One handler for all list items
if (event.target.tagName === 'BUTTON') {
console.log('Button clicked:', event.target.dataset.id);
}
};
return (
<ul onClick={handleListClick}>
<li><button data-id="1">Item 1</button></li>
<li><button data-id="2">Item 2</button></li>
<li><button data-id="3">Item 3</button></li>
</ul>
);
}
React reuses event objects:
// Problem with event pooling
function ProblematicComponent() {
const handleClick = (event) => {
// ❌ event will be null after synchronous code
setTimeout(() => {
console.log(event.target); // undefined!
}, 0);
};
return <button onClick={handleClick}>Problematic button</button>;
}
// ✅ Solution with persist()
function GoodComponent() {
const handleClick = (event) => {
// Preserve event for asynchronous use
event.persist();
setTimeout(() => {
console.log(event.target); // Works!
}, 0);
};
return <button onClick={handleClick}>Good button</button>;
}
function EventComparison() {
const syntheticHandler = (syntheticEvent) => {
// Synthetic Event
console.log(syntheticEvent.target);
console.log(syntheticEvent.type);
// syntheticEvent.stopPropagation() - works
// syntheticEvent.preventDefault() - works
};
const nativeHandler = () => {
// Native event
const nativeEvent = document.getElementById('myButton')
.addEventListener('click', (event) => {
console.log(event.target);
console.log(event.type);
// event.stopPropagation() - works
// event.preventDefault() - works
});
};
return <button onClick={syntheticHandler} id="myButton">Comparison</button>;
}
function AccessingNativeEvent() {
const handleClick = (syntheticEvent) => {
// Access native event
const nativeEvent = syntheticEvent.nativeEvent;
console.log('Synthetic:', syntheticEvent);
console.log('Native:', nativeEvent);
// Both events work, but have different properties
};
return <button onClick={handleClick}>Access native event</button>;
}
function ThirdPartyIntegration() {
const divRef = useRef(null);
useEffect(() => {
// Some libraries require native events
const jqueryElement = $(divRef.current);
jqueryElement.on('customEvent', (nativeEvent) => {
// Native event needed here
console.log(nativeEvent);
});
return () => {
jqueryElement.off('customEvent');
};
}, []);
return <div ref={divRef}>jQuery integration</div>;
}
function SpecificEventProperties() {
const handleTouch = (syntheticEvent) => {
// Some specific properties are only available in native event
const nativeEvent = syntheticEvent.nativeEvent;
if (nativeEvent.touches) {
console.log('Touch count:', nativeEvent.touches.length);
console.log('First touch:', nativeEvent.touches[0]);
}
};
return <div onTouchMove={handleTouch}>Touch surface</div>;
}
// ❌ Common mistake
function BadAsyncAccess() {
const handleClick = (event) => {
// event will be null in asynchronous code
fetch('/api/data').then(() => {
console.log(event.target); // ❌ undefined!
});
};
return <button onClick={handleClick}>Error</button>;
}
// ✅ Correct solution
function GoodAsyncAccess() {
const handleClick = (event) => {
// Save reference to target
const target = event.target;
fetch('/api/data').then(() => {
console.log(target); // ✅ Works!
});
};
return <button onClick={handleClick}>Correct</button>;
}
// ❌ Bad practice
function BadMixing() {
const handleClick = (syntheticEvent) => {
// Don't mix synthetic and native events unnecessarily
syntheticEvent.nativeEvent.stopPropagation();
syntheticEvent.preventDefault();
};
return <button onClick={handleClick}>Bad mix</button>;
}
// ✅ Good practice
function GoodPractice() {
const handleClick = (syntheticEvent) => {
// Use Synthetic Event API
syntheticEvent.stopPropagation();
syntheticEvent.preventDefault();
};
return <button onClick={handleClick}>Good practice</button>;
}
Synthetic Events are like a universal translator between your code and browsers! 🌍
When to use what:
Practical tips:
event.persist()
only when necessarySynthetic Events are one of the reasons why React is so popular: it takes the complexity of cross-browser compatibility off your shoulders, allowing you to focus on application logic! 💪
Want more useful React articles? Subscribe to EasyAdvice, bookmark the site and level up every day! 🚀