Реакция на события¶
React позволяет добавлять обработчики событий в JSX. Обработчики событий — это ваши собственные функции, которые будут запускаться в ответ на такие взаимодействия, как нажатие, наведение курсора, фокусировка ввода формы и так далее.
Вы узнаете
- Различные способы написания обработчика событий
- Как передать логику обработки событий от родительского компонента
- Как распространяются события и как их остановить
Добавление обработчиков событий¶
Чтобы добавить обработчик событий, вы сначала определите функцию, а затем передадите ее как параметр в соответствующий JSX тег. Например, вот кнопка, которая еще ничего не делает:
1 2 3 |
|
Вы можете заставить его показывать сообщение, когда пользователь нажимает на кнопку, выполнив следующие три шага:
- Объявите функцию
handleClick
внутри вашего компонентаButton
. - Реализуйте логику внутри этой функции (используйте
alert
для показа сообщения). - Добавьте
onClick={handleClick}
в JSX<button>
.
1 2 3 4 5 6 7 |
|
Вы определили функцию handleClick
, а затем передали ее как параметр в <button>
. handleClick
- это обработчик события. Функции обработчика события:
- Обычно определяются внутри ваших компонентов.
- Имеют имена, начинающиеся с
handle
, за которым следует имя события.
По традиции, принято называть обработчики событий handle
, за которым следует имя события. Часто можно встретить onClick={handleClick}
, onMouseEnter={handleMouseEnter}
и так далее.
В качестве альтернативы вы можете определить обработчик события в JSX:
1 2 3 |
|
Или, более кратко, с помощью стрелочной функции:
1 2 3 |
|
Все эти стили эквивалентны. Инлайн-обработчики событий удобны для коротких функций.
Внимание
Функции, передаваемые в обработчики событий, должны передаваться, а не вызываться. Например:
передача функции (правильно) | вызов функции (неправильно) |
---|---|
<button onClick={handleClick}> | <button onClick={handleClick()}> |
Разница очень тонкая. В первом примере функция handleClick
передается как обработчик события onClick
. Это говорит React запомнить ее и вызывать вашу функцию только тогда, когда пользователь нажмет на кнопку.
Во втором примере ()
в конце handleClick()
запускает функцию непосредственно во время rendering, без каких-либо кликов. Это происходит потому, что JavaScript внутри JSX {
и }
выполняется сразу же.
Когда вы пишете код inline, тот же самый подводный камень проявляется по-другому:
передача функции (правильно) | вызов функции (неправильно) |
---|---|
<button onClick={() => alert('...')}> | <button onClick={alert('...')}> |
Передача встроенного кода таким образом не срабатывает по щелчку - он срабатывает каждый раз, когда компонент отображается:
1 2 |
|
Если вы хотите определить обработчик события в строке, оберните его в анонимную функцию, как показано ниже:
1 |
|
Вместо того чтобы выполнять внутренний код при каждом рендере, создается функция, которая будет вызвана позже.
В обоих случаях вы хотите передать функцию:
<button onClick={handleClick}>
передает функциюhandleClick
.<button onClick={() => alert('...')}>
передает функцию() => alert('...')
.
Чтение пропсов в обработчиках событий¶
Поскольку обработчики событий объявлены внутри компонента, они имеют доступ к пропсам компонента. Вот кнопка, которая при нажатии показывает оповещение со своим пропсом message
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Это позволяет этим двум кнопкам показывать разные сообщения. Попробуйте изменить передаваемые им сообщения.
Передача обработчиков событий как пропсов¶
Часто требуется, чтобы родительский компонент указывал обработчик событий дочернего компонента. Рассмотрим кнопки: в зависимости от того, где вы используете компонент Button
, вы можете захотеть выполнить разные функции - возможно, одна воспроизводит фильм, а другая загружает изображение.
Для этого в качестве обработчика события передайте пропс, который компонент получает от своего родителя, следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
Здесь компонент Toolbar
отображает PlayButton
и UploadButton
:
PlayButton
передаетhandlePlayClick
как свойствоonClick
дляButton
внутри.UploadButton
передает() => alert('Uploading!')
как свойствоonClick
внутреннейButton
.
Наконец, ваш компонент Button
принимает свойство onClick
. Он передает это свойство непосредственно встроенному браузеру <button>
с onClick={onClick}
. Это указывает React на вызов переданной функции по щелчку.
Если вы используете систему дизайна, то обычно такие компоненты, как кнопки, содержат стиль, но не определяют поведение. Вместо этого такие компоненты, как PlayButton
и UploadButton
, передают обработчики событий вниз.
Именование параметров обработчика событий¶
Встроенные компоненты, такие как <button>
и <div>
, поддерживают только имена событий браузера, такие как onClick
. Однако, когда вы создаете свои собственные компоненты, вы можете называть их пропсы обработчиков событий как угодно.
По соглашению, пропсы обработчиков событий должны начинаться с on
, за которым следует заглавная буква.
Например, пропс onClick
компонента Button
можно было бы назвать onSmash
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
В этом примере <button onClick={onSmash}>
показывает, что браузеру <button>
(строчная буква) по-прежнему нужен пропс onClick
, но имя пропса, полученное вашим пользовательским компонентом Button
, зависит от вас!
Если ваш компонент поддерживает множество взаимодействий, вы можете назвать пропсы обработчиков событий для концепций, специфичных для конкретного приложения. Например, компонент Toolbar
получает обработчики событий onPlayMovie
и onUploadImage
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Обратите внимание, что компоненту App
не нужно знать, что Toolbar
будет делать с onPlayMovie
или onUploadImage
. Это деталь реализации Toolbar
. Здесь Toolbar
передает их как обработчики onClick
для своих Button
, но позже он также может вызвать их по нажатию клавиш. Именование пропсов в честь специфических для приложения взаимодействий, таких как onPlayMovie
, дает вам возможность гибко изменять их использование в дальнейшем.
Убедитесь, что вы используете соответствующие HTML-теги для обработчиков событий. Например, для обработки кликов используйте <button onClick={handleClick}>
вместо <div onClick={handleClick}>
. Использование настоящего браузерного <button>
позволяет использовать встроенные поведенческие характеристики браузера, такие как навигация по клавиатуре. Если вам не нравится стандартная стилизация кнопки в браузере и вы хотите сделать ее более похожей на ссылку или другой элемент пользовательского интерфейса, вы можете добиться этого с помощью CSS. Узнайте больше о написании доступной разметки.
Распространение событий¶
Обработчики событий также будут улавливать события от всех дочерних компонентов, которые могут быть у вашего компонента. Мы говорим, что событие "бурлит" или "распространяется" вверх по дереву: оно начинается с того места, где произошло событие, а затем поднимается вверх по дереву.
Этот <div>
содержит две кнопки. И <div>
и каждая кнопка имеют свои собственные обработчики onClick
. Как вы думаете, какие обработчики сработают при нажатии на кнопку?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Если вы нажмете на любую из кнопок, сначала сработает ее onClick
, а затем onClick
родительской <div>
. Таким образом, появятся два сообщения. Если щелкнуть на самой панели инструментов, то будет запущена только onClick
родительской <div>
.
Внимание
Все события распространяются в React, кроме onScroll
, которое действует только на тег JSX, к которому вы его прикрепили.
Остановка распространения¶
Обработчики событий получают объект event в качестве единственного аргумента. По традиции он обычно называется e
, что означает "событие". Вы можете использовать этот объект для чтения информации о событии.
Этот объект события также позволяет остановить распространение. Если вы хотите, чтобы событие не достигло родительских компонентов, вам нужно вызвать e.stopPropagation()
, как это делает компонент Button
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Когда вы нажимаете на кнопку:
- React вызывает обработчик
onClick
, переданный в<button>
. - Этот обработчик, определенный в
Button
, делает следующее:- Вызывает
e.stopPropagation()
, предотвращая дальнейшее распространение события. - Вызывает функцию
onClick
, которая является пропсом, переданным из компонентаToolbar
.
- Вызывает
- Эта функция, определенная в компоненте
Toolbar
, отображает собственное оповещение кнопки. - Поскольку распространение было остановлено, обработчик
onClick
родительского<div>
не выполняется.
В результате e.stopPropagation()
, при нажатии на кнопки теперь отображается только одно оповещение (из <button>
), а не оба (из <button>
и родительской панели инструментов <div>
). Нажатие на кнопку - это не то же самое, что нажатие на окружающую панель инструментов, поэтому остановка распространения имеет смысл для данного пользовательского интерфейса.
Захват фазовых событий
В редких случаях вам может понадобиться перехватить все события на дочерних элементах, даже если они прекратили распространение. Например, вы хотите регистрировать каждый клик в аналитике, независимо от логики распространения. Вы можете сделать это, добавив Capture
в конце имени события:
1 2 3 4 5 6 7 8 |
|
Каждое событие распространяется в три фазы:
- Оно перемещается вниз, вызывая все обработчики
onClickCapture
. - Запускается обработчик
onClick
щелкнутого элемента. - Он перемещается вверх, вызывая все обработчики
onClick
.
События захвата полезны для такого кода, как маршрутизаторы или аналитика, но вы, вероятно, не будете использовать их в коде приложений.
Передача обработчиков как альтернатива распространению¶
Обратите внимание, как этот обработчик кликов выполняет строку кода и затем вызывает onClick
, переданный родителем:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Вы можете добавить больше кода в этот обработчик перед вызовом обработчика родительского события onClick
. Этот паттерн обеспечивает альтернативу распространению. Он позволяет дочернему компоненту обрабатывать событие, в то же время позволяя родительскому компоненту задать дополнительное поведение. В отличие от распространения, он не является автоматическим. Но преимущество этого паттерна в том, что вы можете четко проследить всю цепочку кода, который выполняется в результате какого-то события.
Если вы полагаетесь на распространение и вам сложно отследить, какие обработчики выполняются и почему, попробуйте применить этот подход.
Предотвращение поведения по умолчанию¶
Некоторые события браузера имеют поведение по умолчанию, связанное с ними. Например, событие отправки <form>
, которое происходит при нажатии на кнопку внутри него, по умолчанию перезагружает всю страницу:
1 2 3 4 5 6 7 8 |
|
Чтобы этого не произошло, можно вызвать e.preventDefault()
на объекте события:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Не путайте e.stopPropagation()
и e.preventDefault()
. Они оба полезны, но не связаны между собой:
e.stopPropagation()
останавливает срабатывание обработчиков событий, прикрепленных к вышеуказанным тегам.e.preventDefault()
предотвращает поведение браузера по умолчанию для тех немногих событий, в которых он есть.
Могут ли обработчики событий иметь побочные эффекты?¶
Конечно! Обработчики событий - лучшее место для побочных эффектов.
В отличие от функций рендеринга, обработчики событий не обязаны быть чистыми, поэтому это отличное место для изменения чего-либо - например, изменения значения ввода в ответ на ввод текста или изменения списка в ответ на нажатие кнопки. Однако для того, чтобы изменить какую-то информацию, вам сначала нужно каким-то образом ее сохранить. В React для этого используется state, память компонента. Вы узнаете все об этом на следующей странице.
Итоги
- Вы можете обрабатывать события, передавая функцию в качестве пропса элементу, например
<button>
. - Обработчики событий должны передаваться, а не вызываться!
onClick={handleClick}
, а неonClick={handleClick()}
. - Вы можете определить функцию-обработчик события отдельно или в строке.
- Обработчики событий определяются внутри компонента, поэтому они могут обращаться к пропсам.
- Вы можете объявить обработчик событий в родительском компоненте и передать его в качестве пропса дочернему компоненту.
- Вы можете определить собственные пропсы обработчика событий с именами, специфичными для конкретного приложения.
- События распространяются вверх. Чтобы предотвратить это, вызовите
e.stopPropagation()
для первого аргумента. - События могут иметь нежелательное поведение браузера по умолчанию. Вызовите
e.preventDefault()
, чтобы предотвратить это. - Явный вызов свойства обработчика события из дочернего обработчика является хорошей альтернативой распространению.
Задачи¶
1. Исправьте обработчик события¶
Щелчок на этой кнопке должен переключить фон страницы между белым и черным. Однако при нажатии ничего не происходит. Исправьте проблему. (Не беспокойтесь о логике внутри handleClick
- эта часть в порядке).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Показать решение
Проблема в том, что <button onClick={handleClick()}>
вызывает функцию handleClick
во время рендеринга вместо того, чтобы передать ее. Удаление вызова ()
, чтобы было <button onClick={handleClick}>
устраняет проблему:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
В качестве альтернативы можно обернуть вызов в другую функцию, например <button onClick={() => handleClick()}>
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
2. Подключение событий¶
Этот компонент ColorSwitch
отображает кнопку. Он должен менять цвет страницы. Подключите его к обработчику события onChangeColor
, который он получает от родителя, чтобы щелчок по кнопке изменил цвет.
После того, как вы это сделаете, обратите внимание, что нажатие на кнопку также увеличивает счетчик нажатий на страницу. Ваш коллега, написавший родительский компонент, настаивает, что onChangeColor
не увеличивает никаких счетчиков. Что еще может происходить? Исправьте это так, чтобы нажатие на кнопку только изменяло цвет и не увеличивало счетчик.
1 2 3 |
|
Показать решение
Сначала нужно добавить обработчик события, например <button onClick={onChangeColor}>
.
Однако это влечет за собой проблему увеличивающегося счетчика. Если onChangeColor
не делает этого, как настаивает ваш коллега, то проблема в том, что это событие распространяется вверх, и какой-то обработчик выше делает это. Чтобы решить эту проблему, нужно остановить распространение. Но не забывайте, что вы все равно должны вызвать onChangeColor
.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Источник — https://react.dev/learn/responding-to-events