Синтетичні події в React
Що таке Синтетичні Події?
Синтетичні Події — це обгортка React навколо рідних подій браузера. React створює єдину систему подій, яка працює однаково в усіх браузерах.
function Button() {
function handleClick(event) {
console.log(event); // SyntheticEvent, а не native Event
}
return <button onClick={handleClick}>Натисни на мене</button>;
}Чому вони потрібні?
Кросбраузерна сумісність
Різні браузери мають різні реалізації подій. Синтетичні Події уніфікують API:
function Input() {
function handleChange(event) {
// event.target.value працює однаково скрізь
console.log(event.target.value);
}
return <input onChange={handleChange} />;
}Продуктивність
React використовує делегування подій: всі обробники прикріплюються до кореневого елемента, а не до кожного елемента окремо.
// Замість 1000 обробників на кожній кнопці
// React створює один обробник на корені
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>
<button onClick={() => console.log(item.id)}>
{item.name}
</button>
</li>
))}
</ul>
);
}API Синтетичних Подій
Синтетична Подія має той же інтерфейс, що й рідні події:
function Form() {
function handleSubmit(event) {
event.preventDefault(); // Працює як рідна подія
event.stopPropagation();
console.log(event.type); // 'submit'
console.log(event.target); // посилання на елемент
console.log(event.currentTarget); // елемент з обробником
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Відправити</button>
</form>
);
}Пулінг Подій (до React 17)
У React 16 і раніше події повторно використовувалися для підвищення продуктивності:
// React 16 і раніше
function handleClick(event) {
console.log(event.type); // 'click'
setTimeout(() => {
console.log(event.type); // null, подія очищена!
}, 0);
}Щоб зберегти подію, ви використовували event.persist():
function handleClick(event) {
event.persist(); // Зберегти подію
setTimeout(() => {
console.log(event.type); // 'click', працює!
}, 0);
}React 17+:
У React 17 пулінг подій був видалений. Події більше не очищуються, event.persist() не потрібен.
Доступ до рідної події
Ви можете отримати рідну подію через nativeEvent:
function handleClick(event) {
console.log(event); // SyntheticEvent
console.log(event.nativeEvent); // Рідна подія браузера
// Корисно для специфічних функцій браузера
console.log(event.nativeEvent.path);
}Відмінності від рідних подій
Іменування
React використовує camelCase замість малих літер:
// HTML
<button onclick="handleClick()">Натисни</button>
// React
<button onClick={handleClick}>Натисни</button>Повернення false
В HTML ви можете повернути false, щоб запобігти стандартній дії:
<!-- HTML -->
<a href="#" onclick="console.log('натиснуто'); return false">
Посилання
</a>В React ви повинні явно викликати preventDefault():
// React
function Link() {
function handleClick(event) {
event.preventDefault();
console.log('натиснуто');
}
return <a href="#" onClick={handleClick}>Посилання</a>;
}Підтримувані події
React підтримує всі стандартні DOM події:
Мишині події
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');
}
}
return <input onKeyDown={handleKeyDown} />;
}Події форм
function Form() {
return (
<form
onSubmit={e => e.preventDefault()}
onChange={e => console.log('змінено')}
onFocus={e => console.log('в фокусі')}
onBlur={e => console.log('втрата фокусу')}
>
<input type="text" />
</form>
);
}Події фокусу
onFocus
onBlurСенсорні події
onTouchStart
onTouchMove
onTouchEnd
onTouchCancelДелегування подій
React 16 і раніше
Події прикріплювалися до document:
// React прикріплював обробники до document
document.addEventListener('click', handleAllClicks);React 17+
Події прикріплюються до кореневого вузла React:
// React прикріплює до кореня додатку
const root = document.getElementById('root');
root.addEventListener('click', handleAllClicks);Це важливо при використанні кількох версій React на одній сторінці або інтеграції з іншими бібліотеками.
Специфіка обробки подій
onChange проти onInput
В React onChange працює як рідний onInput — спрацьовує на кожну зміну:
function Input() {
const [value, setValue] = useState('');
return (
<input
value={value}
// Спрацьовує на кожен натиск клавіші
onChange={e => setValue(e.target.value)}
/>
);
}onScroll
onScroll не спрацьовує в React (як в DOM), але React надає його для зручності:
function ScrollableDiv() {
function handleScroll(event) {
console.log(event.target.scrollTop);
}
return (
<div onScroll={handleScroll} style={{ height: 200, overflow: 'auto' }}>
{/* Багато контенту */}
</div>
);
}Пряме прикріплення рідного обробника
Іноді вам потрібно прикріпити рідний обробник безпосередньо:
function Component() {
const ref = useRef(null);
useEffect(() => {
const element = ref.current;
function handleNativeClick(event) {
console.log('Рідний клік', event);
}
element.addEventListener('click', handleNativeClick);
return () => {
element.removeEventListener('click', handleNativeClick);
};
}, []);
return <div ref={ref}>Натисни на мене</div>;
}Попередження:
Будьте обережні з прямими обробниками — не забудьте видалити їх у функції очищення.
Захоплення подій
React підтримує фазу захоплення:
function Parent() {
return (
<div
onClickCapture={() => console.log('1. Захоплення батька')}
onClick={() => console.log('3. Бульбашка батька')}
>
<button
onClickCapture={() => console.log('2. Захоплення кнопки')}
onClick={() => console.log('4. Бульбашка кнопки')}
>
Натисни
</button>
</div>
);
}
// Натискання кнопки виведе:
// 1. Захоплення батька
// 2. Захоплення кнопки
// 4. Бульбашка кнопки
// 3. Бульбашка батькаТипізація TypeScript
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}>Відправити</button>
</form>
);
}Загальні помилки
Асинхронне використання подій (React 16)
// Неправильно в React 16
function handleClick(event) {
setTimeout(() => {
console.log(event.type); // null!
}, 0);
}
// Правильно
function handleClick(event) {
const eventType = event.type; // Зберегти значення
setTimeout(() => {
console.log(eventType); // 'click'
}, 0);
}Передача події до setState
// Неправильно
function handleChange(event) {
setState(prevState => ({
...prevState,
value: event.target.value // подія може бути очищена
}));
}
// Правильно
function handleChange(event) {
const newValue = event.target.value;
setState(prevState => ({
...prevState,
value: newValue
}));
}Висновок
Синтетичні Події:
- Обгортка навколо рідних подій браузера
- Забезпечують кросбраузерну сумісність
- Використовують делегування для продуктивності
- Мають той же API, що й рідні події
- У React 17+ не очищуються автоматично
- Використовують camelCase для іменування
- Рідна подія доступна через
nativeEvent
На співбесідах:
Важливо вміти:
- Пояснити, що таке Синтетичні Події і чому вони потрібні
- Описати делегування подій
- Пояснити відмінності від рідних подій
- Описати зміни в React 17 (пулінг подій)
- Показати, як отримати доступ до рідної події
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.