Loading...
Loading...
By continuing to use the platform, you accept the terms of the Privacy Policy and the use of cookies.
👋 Hi! I'm Dastan, author of Hack Frontend. Always open to professional networking => connect with me on LinkedIn.
Synthetic Events are React's wrapper around native browser events. React creates a unified event system that works the same across all browsers.
function Button() {
function handleClick(event) {
console.log(event); // SyntheticEvent, not native Event
}
return <button onClick={handleClick}>Click me</button>;
}
Different browsers have different event implementations. Synthetic Events unify the API:
function Input() {
function handleChange(event) {
// event.target.value works the same everywhere
console.log(event.target.value);
}
return <input onChange={handleChange} />;
}
React uses event delegation: all handlers are attached to the root element, not to each element separately.
// Instead of 1000 handlers on each button
// React creates one handler at the root
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>
<button onClick={() => console.log(item.id)}>
{item.name}
</button>
</li>
))}
</ul>
);
}
Synthetic Event has the same interface as native events:
function Form() {
function handleSubmit(event) {
event.preventDefault(); // Works like native event
event.stopPropagation();
console.log(event.type); // 'submit'
console.log(event.target); // element reference
console.log(event.currentTarget); // element with handler
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
In React 16 and earlier, events were reused for performance:
// React 16 and earlier
function handleClick(event) {
console.log(event.type); // 'click'
setTimeout(() => {
console.log(event.type); // null, event cleared!
}, 0);
}
To preserve an event, you used event.persist():
function handleClick(event) {
event.persist(); // Preserve event
setTimeout(() => {
console.log(event.type); // 'click', works!
}, 0);
}
React 17+:
In React 17, event pooling was removed. Events are no longer cleared, event.persist() is not needed.
You can get the native event through nativeEvent:
function handleClick(event) {
console.log(event); // SyntheticEvent
console.log(event.nativeEvent); // Native browser Event
// Useful for browser-specific features
console.log(event.nativeEvent.path);
}
React uses camelCase instead of lowercase:
// HTML
<button onclick="handleClick()">Click</button>
// React
<button onClick={handleClick}>Click</button>
In HTML you can return false to prevent default action:
<!-- HTML -->
<a href="#" onclick="console.log('clicked'); return false">
Link
</a>
In React you must explicitly call preventDefault():
// React
function Link() {
function handleClick(event) {
event.preventDefault();
console.log('clicked');
}
return <a href="#" onClick={handleClick}>Link</a>;
}
React supports all standard DOM events:
onClick
onContextMenu
onDoubleClick
onDrag
onDragEnd
onDragEnter
onDragExit
onDragLeave
onDragOver
onDragStart
onDrop
onMouseDown
onMouseEnter
onMouseLeave
onMouseMove
onMouseOut
onMouseOver
onMouseUp
function Input() {
function handleKeyDown(event) {
if (event.key === 'Enter') {
console.log('Enter pressed');
}
}
return <input onKeyDown={handleKeyDown} />;
}
function Form() {
return (
<form
onSubmit={e => e.preventDefault()}
onChange={e => console.log('changed')}
onFocus={e => console.log('focused')}
onBlur={e => console.log('blurred')}
>
<input type="text" />
</form>
);
}
onFocus
onBlur
onTouchStart
onTouchMove
onTouchEnd
onTouchCancel
Events were attached to document:
// React attached handlers to document
document.addEventListener('click', handleAllClicks);
Events are attached to the React root node:
// React attaches to app root
const root = document.getElementById('root');
root.addEventListener('click', handleAllClicks);
This is important when using multiple React versions on one page or integrating with other libraries.
In React onChange works like native onInput — fires on every change:
function Input() {
const [value, setValue] = useState('');
return (
<input
value={value}
// Fires on every keystroke
onChange={e => setValue(e.target.value)}
/>
);
}
onScroll doesn't bubble in React (as in DOM), but React provides it for convenience:
function ScrollableDiv() {
function handleScroll(event) {
console.log(event.target.scrollTop);
}
return (
<div onScroll={handleScroll} style={{ height: 200, overflow: 'auto' }}>
{/* Lots of content */}
</div>
);
}
Sometimes you need to attach a native handler directly:
function Component() {
const ref = useRef(null);
useEffect(() => {
const element = ref.current;
function handleNativeClick(event) {
console.log('Native click', event);
}
element.addEventListener('click', handleNativeClick);
return () => {
element.removeEventListener('click', handleNativeClick);
};
}, []);
return <div ref={ref}>Click me</div>;
}
Warning:
Be careful with direct handlers — don't forget to remove them in cleanup function.
React supports the capture phase:
function Parent() {
return (
<div
onClickCapture={() => console.log('1. Parent capture')}
onClick={() => console.log('3. Parent bubble')}
>
<button
onClickCapture={() => console.log('2. Button capture')}
onClick={() => console.log('4. Button bubble')}
>
Click
</button>
</div>
);
}
// Clicking button outputs:
// 1. Parent capture
// 2. Button capture
// 4. Button bubble
// 3. Parent bubble
import { MouseEvent, ChangeEvent, FormEvent } from 'react';
function Component() {
function handleClick(event: MouseEvent<HTMLButtonElement>) {
console.log(event.currentTarget.name);
}
function handleChange(event: ChangeEvent<HTMLInputElement>) {
console.log(event.target.value);
}
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
<button onClick={handleClick}>Submit</button>
</form>
);
}
// Wrong in React 16
function handleClick(event) {
setTimeout(() => {
console.log(event.type); // null!
}, 0);
}
// Correct
function handleClick(event) {
const eventType = event.type; // Save value
setTimeout(() => {
console.log(eventType); // 'click'
}, 0);
}
// Wrong
function handleChange(event) {
setState(prevState => ({
...prevState,
value: event.target.value // event might be cleared
}));
}
// Correct
function handleChange(event) {
const newValue = event.target.value;
setState(prevState => ({
...prevState,
value: newValue
}));
}
Synthetic Events:
nativeEventIn interviews:
Important to be able to: