React обрабатывает события через пропсы, названные по событию в camelCase, например onClick, onChange и onSubmit. Вы передаёте функцию, а React вызывает её с объектом события, когда пользователь кликает, печатает или отправляет форму.
Каждый клик запускает handleClick, который обновляет счётчик и пишет в консоль под превью. Попробуйте onDoubleClick вместо onClick: теперь считается только двойной клик.
Передавайте функцию, а не вызывайте её
Пропс принимает функцию, а React вызывает её позже. Очень частая ошибка это добавить круглые скобки: функция вызывается сразу, во время рендера, и React получает её возвращаемое значение.
Консоль показывает "Hi, during render" ещё до того, как вы что-то нажали: React выполнил sayHi во время рендера первой кнопки, а поскольку sayHi возвращает undefined, у этой кнопки вообще нет обработчика. Клик по Wrong ничего не делает. Клик по Right выводит "Hi, Ada", потому что стрелочная функция выполняется только при клике.
Стрелка также служит для передачи аргументов. onClick={() => removeItem(item.id)} создаёт небольшую функцию для каждого элемента, и эта функция вызывает вашу с правильным ID. Если обработчик не принимает аргументов, передавайте его по имени: onClick={handleClick}.
С сеттером состояния ошибка хуже: onClick={setCount(count + 1)} устанавливает состояние во время рендера, что вызывает новый рендер, который снова устанавливает состояние, и React останавливается с "Too many re-renders".
Объект события
React передаёт каждому обработчику один аргумент, объект события. У него те же свойства, что и у события браузера: e.target это элемент, на котором произошло событие, e.key это нажатая клавиша, e.preventDefault() отменяет действие браузера по умолчанию. React оборачивает нативное событие в то, что он называет синтетическим событием, поэтому оно ведёт себя одинаково во всех браузерах; оригинал лежит в e.nativeEvent, если он когда-нибудь понадобится.
Чаще всего используются два: e.target.value в onChange и e.key в onKeyDown.
Наберите hooks и нажмите Enter: тег добавится, а поле очистится. Консоль выводит каждую клавишу, включая Shift, Backspace и Enter, под именами из e.key. Нажмите Escape посреди слова, чтобы отбросить его.
onChange в React срабатывает при каждом нажатии клавиши. В обычном HTML событие change у текстового поля срабатывает только при потере фокуса, так что это намеренное отличие: оно позволяет полю и состоянию идти в ногу буква за буквой. Флажки дают e.target.checked вместо value. Все типы полей разобраны на странице о формах.
Формы и preventDefault
Когда форма отправляется, браузер по умолчанию отправляет её и загружает новую страницу. В приложении React почти всегда нужно обработать данные самостоятельно, поэтому обработчик отправки начинается с e.preventDefault().
Ставьте onSubmit на <form>, а не onClick на кнопку: тогда нажатие Enter в поле тоже отправляет форму, а собственные проверки браузера (здесь required и type="email") выполняются первыми. Удалите строку e.preventDefault() и отправьте снова: настоящая страница теперь ушла бы на новую страницу и потеряла бы сообщение с благодарностью. Превью блокирует это и вместо этого показывает, что отправила бы форма: email= без значения, потому что setEmail('') уже очистил поле к моменту чтения формы.
React 19 также позволяет передать функцию в <form action={fn}>, которая получает данные формы и сама сбрасывает неконтролируемые поля. Страница о формах показывает оба стиля.
Всплытие и stopPropagation
Большинство событий всплывают: выполнившись на элементе, по которому вы кликнули, они выполняются на его родителе, затем на родителе родителя, до самого верха. Обработчики React следуют тому же порядку. Поэтому клик по кнопке внутри кликабельной карточки считается и кликом по карточке.
Нажмите Bubbles up, и консоль выведет кнопку, затем внутреннюю панель, затем внешнюю карточку. Нажмите Stops here, и выведется только кнопка: e.stopPropagation() прекращает подъём. Клик по собственному тексту внутренней панели выводит панель и карточку.
Используйте stopPropagation экономно. Частый оправданный случай это кнопка удаления внутри кликабельной строки списка, где клик по удалению не должен заодно открывать строку. Если родителю нужно знать, пришёл ли клик от дочернего элемента, проверяйте e.target, а не останавливайте событие. Чтобы запустить обработчик на пути вниз, до того как событие увидят дети, React предлагает версии для фазы перехвата, например onClickCapture.
preventDefault и stopPropagation это разные инструменты. Первый отменяет то, что сделал бы браузер (перейти по ссылке, отправить форму, отметить флажок). Второй не даёт другим обработчикам узнать о событии. Вызов одного не делает другого.
Другие частые события
Тот же шаблон работает для каждого события, которое поддерживает React. Несколько, которыми вы будете пользоваться часто:
onFocusиonBlurсрабатывают, когда поле получает и теряет фокус; удобно, чтобы показать валидацию после того, как пользователь покинул поле.onMouseEnterиonMouseLeaveсрабатывают, когда указатель наводится на элемент и уходит с него. В отличие от большинства событий они не всплывают, поэтому переход указателя из родителя в его дочерний элемент не вызываетonMouseEnterродителя повторно.onPointerDown,onPointerMoveиonPointerUpработают одинаково для мыши, касания и пера, поэтому лучше подходят для перетаскивания.onScrollсрабатывает при прокрутке элемента. В React оно не всплывает.
Каждый обработчик получает объект события со свойствами, которые для него имеют смысл: e.clientX для событий указателя, e.key для событий клавиатуры, e.target для всех.
Обработчики как пропсы
Компоненты могут принимать обработчики от родителя, как любой другой пропс. Называйте их onSomething по тому, что произошло, а функцию, которая это обрабатывает, handleSomething. Это повторяет встроенные события и сразу показывает, какие пропсы являются колбэками.
Stepper не владеет числом; он сообщает о кликах, а родитель решает, что они значат. Измените handleDecrement, чтобы он останавливался на 1, а не на 0, и степпер последует за ним без правок. Хранение состояния в родителе и передача обработчиков вниз это шаблон, описанный в разделе о подъёме состояния.
Пользовательские пропсы-обработчики это просто имена. onIncrement у вашего компонента ничего не делает, пока компонент не передаст его в onClick настоящего элемента, и вы можете передавать ему любые аргументы, например onSelect(item.id).
Часто задаваемые вопросы
Как передать параметр в обработчик onClick?
Оберните вызов в стрелочную функцию: onClick={() => deleteItem(item.id)}. Именно стрелку React вызывает при клике, а она затем вызывает вашу функцию с аргументом.
Почему мой onClick срабатывает при загрузке страницы?
Вы вызвали функцию, а не передали её. onClick={handleClick()} выполняет handleClick во время рендера и передаёт её возвращаемое значение. Пишите onClick={handleClick} или onClick={() => handleClick(id)}.
Чем onChange в React отличается от HTML?
В HTML событие change у текстового поля срабатывает, когда поле теряет фокус. onChange в React срабатывает при каждом нажатии клавиши, как нативное событие input, поэтому состояние может следовать за текстом по мере ввода.
Как запретить форме перезагружать страницу в React?
Вызовите e.preventDefault() в начале обработчика onSubmit. Ставьте обработчик на <form>, а не на кнопку отправки, чтобы нажатие Enter в поле тоже отправляло форму.
Что такое синтетическое событие в React?
Это объект события, который React передаёт вашим обработчикам. Он оборачивает нативное событие браузера с теми же свойствами и методами (target, key, preventDefault(), stopPropagation()) и ведёт себя одинаково во всех браузерах. Оригинал доступен как e.nativeEvent.