React
События и обработчики
Интерфейс без реакции на действия - это картинка. Клик по кнопке, ввод в поле, отправка формы, наведение мыши - всё это события, и без их обработки приложение мертво. В обычном DOM для этого вызывают addEventListener, следят за тем, чтобы потом отписаться, и борются с различиями браузеров. React убирает эту рутину: обработчик вешается прямо в JSX как атрибут, отписка происходит автоматически, а само событие приходит в обёртке, которая ведёт себя одинаково во всех браузерах. Так клик превращается в изменение состояния, а изменение состояния - в новый кадр интерфейса.
- Каждая кнопка 'Добавить в корзину', каждое поле поиска, каждый сабмит формы в любом React-приложении - это обработчик события
- Drag-and-drop в Trello, рисование в Figma, горячие клавиши в редакторах - всё построено на обработке событий React
- Делегирование событий в корень приложения работает под капотом автоматически, без ручного addEventListener
- Аналитика кликов и пользовательских действий тоже навешивается через те же обработчики onClick и onSubmit
Предварительные знания
- useState: вызов сеттера из обработчика планирует ре-рендер
- JavaScript: функции как значения, стрелочные функции, замыкания
- Базовое представление о событиях DOM (click, input, submit)
Навешивание обработчиков в JSX
Обработчик события задаётся прямо в JSX как атрибут в camelCase: onClick, onChange, onSubmit, onKeyDown. Его значение - функция. Ключевой нюанс: передают саму функцию, а не результат её вызова. onClick={handleClick} ставит функцию на будущий клик. onClick={handleClick()} вызывает функцию немедленно при рендере и кладёт в onClick её возвращаемое значение - частая ошибка новичка.
onClick={handleClick()} с круглыми скобками вызывает обработчик прямо во время рендера, а не при клике. Если внутри он зовёт сеттер состояния, это запускает новый рендер, и получается бесконечный цикл. Скобки в onClick ставят только внутри инлайн-стрелки.
Обработчик - это обычная функция. Её можно объявить в теле компонента (тогда она замыкает доступ к props и состоянию) или передать как инлайн-стрелку прямо в атрибут. Для коротких действий удобнее стрелка, для сложной логики - именованная функция выше return.
В чём разница между onClick={handleClick} и onClick={handleClick()}?
SyntheticEvent, аргумент события и preventDefault
В обработчик первым аргументом приходит объект события. В React это SyntheticEvent - лёгкая обёртка поверх нативного DOM-события. Она нужна, чтобы событие вело себя одинаково во всех браузерах, сглаживая их исторические различия. У SyntheticEvent тот же привычный интерфейс: event.target, event.preventDefault, event.stopPropagation. Доступ к нативному событию при необходимости есть через event.nativeEvent.
preventDefault отменяет стандартное поведение браузера. Самый частый случай - сабмит формы: по умолчанию браузер перезагружает страницу и теряет состояние приложения. event.preventDefault() это останавливает, и обработку берёт на себя JavaScript. Другой пример - отмена перехода по ссылке для кастомной навигации.
Раньше React переиспользовал объекты SyntheticEvent через пулинг, и читать их в асинхронном коде было нельзя без event.persist(). С React 17 пулинг убрали: теперь событие можно спокойно использовать в промисах и таймерах, как обычный объект.
Зачем в обработчике сабмита формы вызывают event.preventDefault()?
Передача обработчиков и аргументов
Обработчики - это обычные функции, поэтому их можно передавать дочерним компонентам через props. Так реализуется обратный поток данных: родитель отдаёт колбэк вниз, ребёнок вызывает его при событии, и управление возвращается родителю. По соглашению props-обработчики называют с префикса on (onClick, onDelete), а функции внутри компонента - с handle (handleClick, handleDelete).
Часто обработчику нужно передать дополнительный аргумент - например, id удаляемого элемента. Напрямую onClick={handleDelete(id)} не сработает: это вызов при рендере. Решение - обернуть в инлайн-стрелку. Стрелка создаёт замыкание, которое запомнит id и вызовет handleDelete только при клике.
Инлайн-стрелка создаётся заново на каждом рендере. В большинстве случаев это безвредно. React Compiler v1.0 умеет стабилизировать такие функции автоматически, поэтому ручная оптимизация через useCallback в новом коде нужна заметно реже.
Как передать обработчику дополнительный аргумент, например id элемента?
Связь с другими темами
События - канал, через который пользователь влияет на состояние. Дальше это раскрывается так:
- Формы — Контролируемые поля реагируют на onChange и читают значение из аргумента события
- Подъём состояния — Колбэк, переданный вниз через props - это обработчик, поднимающий событие к родителю
- useState — Тело обработчика почти всегда заканчивается вызовом сеттера состояния
Итог
- Обработчик вешается прямо в JSX через атрибут в camelCase: onClick, onChange, onSubmit. Значение - функция, а не строка
- В обработчик передают саму функцию (onClick={handle}), а не результат её вызова (onClick={handle()})
- В обработчик приходит SyntheticEvent - обёртка React поверх нативного события, одинаковая во всех браузерах
- preventDefault отменяет поведение браузера по умолчанию, например перезагрузку страницы при сабмите формы
- Дополнительные аргументы передают через замыкание: onClick={() => handleDelete(id)}
- Обработчики передают дочерним компонентам через props, реализуя обратный поток событий вверх
Связанные уроки
- rc-06-usestate — Обработчики событий нужны прежде всего чтобы вызывать сеттер состояния, поэтому useState идёт первым
- rc-08-forms-controlled — Контролируемые формы целиком строятся на onChange и аргументе события - прямое продолжение этого урока
- rc-09-lifting-state — Передача обработчиков вниз через props - механизм обратного потока данных при подъёме состояния