Реагирование на события
React позволяет добавлятьобработчики событийв ваш JSX. Обработчики событий — это ваши собственные функции, которые будут запускаться в ответ на взаимодействия, такие как клики, наведение курсора, фокусировка на полях формы и так далее.
Вы узнаете
- Различные способы написания обработчика событий
- Как передать логику обработки событий из родительского компонента
- Как события всплывают и как их остановить
Добавление обработчиков событий
Чтобы добавить обработчик события, сначала определите функцию, а затемпередайте её как пропсоответствующему JSX-тегу. Например, вот кнопка, которая пока ничего не делает:
Вы можете заставить её показывать сообщение при клике пользователя, выполнив следующие три шага:
- Объявите функцию с именем
handleClickвнутривашего компонентаButton. - Реализуйте логику внутри этой функции (используйте
alertдля показа сообщения). - Добавьте
onClick={handleClick}в JSX тега<button>.
Вы определили функциюhandleClick, а затемпередали её как проп в <button>.handleClick— этообработчик события.Функции-обработчики событий:
- Обычно определяютсявнутриваших компонентов.
- Имеют имена, начинающиеся с
handle, за которым следует название события.
По соглашению, обработчики событий принято называть какhandle, за которым следует имя события. Вы часто будете видетьonClick={handleClick},onMouseEnter={handleMouseEnter}и так далее.
В качестве альтернативы, вы можете определить обработчик события непосредственно в JSX:
Или, более кратко, используя стрелочную функцию:
Все эти стили эквивалентны. Встроенные обработчики событий удобны для коротких функций.
Подводный камень
Функции, передаваемые в обработчики событий, должны быть переданы, а не вызваны. Например:
| передача функции (правильно) | вызов функции (неправильно) |
|---|---|
<button onClick={handleClick}> | <button onClick={handleClick()}> |
Разница тонкая. В первом примере функцияhandleClickпередаётся как обработчик событияonClick. Это говорит React запомнить её и вызвать только тогда, когда пользователь нажмёт на кнопку.
Во втором примере()в концеhandleClick()вызывает функциюнемедленново времярендеринга, без каких-либо кликов. Это происходит потому, что JavaScript внутриJSX { и }выполняется сразу же.
Когда вы пишете код встроенным образом, та же ловушка проявляется по-другому:
| передача функции (правильно) | вызов функции (неправильно) |
|---|---|
<button onClick={() => alert('...')}> | <button onClick={alert('...')}> |
Передача встроенного кода таким образом не будет срабатывать при клике — он будет срабатывать каждый раз при рендеринге компонента:
Если вы хотите определить обработчик события встроенным образом, оберните его в анонимную функцию, вот так:
Вместо выполнения кода внутри при каждом рендере, это создаёт функцию, которая будет вызвана позже.
В обоих случаях вы хотите передать функцию:
<button onClick={handleClick}>передаёт функциюhandleClick.<button onClick={() => alert('...')}>передаёт функцию() => alert('...').
Чтение пропсов в обработчиках событий
Поскольку обработчики событий объявляются внутри компонента, они имеют доступ к пропсам компонента. Вот кнопка, которая при нажатии показывает алерт со своим пропсомmessage:
Это позволяет этим двум кнопкам показывать разные сообщения. Попробуйте изменить передаваемые им сообщения.
Передача обработчиков событий в качестве пропсов
Часто требуется, чтобы родительский компонент определял обработчик события для дочернего компонента. Рассмотрим кнопки: в зависимости от того, где используется компонентButton, может потребоваться выполнить разные функции — например, одну для воспроизведения фильма, а другую для загрузки изображения.
Для этого передайте пропс, который компонент получает от своего родителя, в качестве обработчика события, как показано ниже:
Здесь компонентToolbarрендеритPlayButton и UploadButton:
PlayButtonпередаётhandlePlayClickв качестве пропсаonClickвнутреннему компонентуButton.UploadButtonпередаёт() => alert('Uploading!')в качестве пропсаonClickвнутреннему компонентуButton.
Наконец, ваш компонентButtonпринимает пропс с именемonClick. Он передаёт этот пропс напрямую встроенному браузерному элементу<button>с помощьюonClick={onClick}. Это указывает React вызывать переданную функцию при клике.
Если вы используетедизайн-систему, обычно компоненты, такие как кнопки, содержат стили, но не определяют поведение. Вместо этого компоненты, такие какPlayButton и UploadButton, будут передавать обработчики событий вниз.
Именование пропсов обработчиков событий
Встроенные компоненты, такие как<button> и <div>, поддерживают толькоимена событий браузера, такие какonClick. Однако при создании собственных компонентов вы можете называть пропсы обработчиков событий любым удобным для вас способом.
По соглашению, пропсы обработчиков событий должны начинаться сon, за которым следует заглавная буква.
Например, пропсButtonкомпонентаonClickмог бы называтьсяonSmash:
В этом примере<button onClick={onSmash}>показывает, что браузерному<button>(в нижнем регистре) по-прежнему нужен пропс с именемonClick, но имя пропса, получаемого вашим пользовательским компонентомButton, зависит от вас!
Когда ваш компонент поддерживает несколько взаимодействий, вы можете называть пропсы обработчиков событий в соответствии с концепциями конкретного приложения. Например, этот компонентToolbarполучает обработчики событийonPlayMovie и onUploadImage:
Обратите внимание, что компонентуAppне нужно знать,чтоToolbarбудет делать сonPlayMovieилиonUploadImage. Это детали реализацииToolbar. ЗдесьToolbarпередаёт их как обработчикиonClick своим Button, но позже он также может запускать их по горячей клавише. Именование пропсов в соответствии с конкретными взаимодействиями приложения, такими какonPlayMovie, даёт вам гибкость для изменения способа их использования в будущем.
Примечание
Убедитесь, что вы используете соответствующие HTML-теги для своих обработчиков событий. Например, для обработки кликов используйте<button onClick={handleClick}>вместо<div onClick={handleClick}>. Использование настоящей браузерной кнопки<button>включает встроенное поведение браузера, такое как навигация с клавиатуры. Если вам не нравится стиль кнопки по умолчанию в браузере и вы хотите, чтобы она выглядела как ссылка или другой элемент интерфейса, вы можете добиться этого с помощью CSS.Узнайте больше о написании доступной разметки.
Всплытие событий
Обработчики событий также будут перехватывать события от любых дочерних элементов вашего компонента. Мы говорим, что событие «всплывает» или «распространяется» вверх по дереву: оно начинается с места, где произошло событие, а затем поднимается вверх по дереву.
Этот<div>содержит две кнопки. И<div>, икаждая кнопка имеют свои собственные обработчикиonClick. Как вы думаете, какие обработчики сработают при клике на кнопку?
Если вы кликнете на любую из кнопок, сначала выполнится еёonClick, а затем onClickродительского<div>. Таким образом, появятся два сообщения. Если вы кликнете на саму панель инструментов, выполнится толькоonClickродительского<div>.
Подводный камень
Все события всплывают в React, кромеonScroll, который работает только на JSX-теге, к которому вы его прикрепили.
Остановка всплытия
Обработчики событий получаютобъект событияв качестве единственного аргумента. По соглашению его обычно называютe, что означает «event» (событие). Вы можете использовать этот объект для чтения информации о событии.
Этот объект события также позволяет остановить всплытие. Если вы хотите предотвратить достижение события родительскими компонентами, вам нужно вызватьe.stopPropagation(), как это делает компонентButton:
Когда вы кликаете на кнопку:
- React вызывает обработчик
onClick, переданный в<button>. - Этот обработчик, определённый в
Button, выполняет следующее:- Вызывает
e.stopPropagation(), предотвращая дальнейшее всплытие события. - Вызывает функцию
onClick, которая является пропсом, переданным из компонентаToolbar.
- Вызывает
- Эта функция, определённая в компоненте
Toolbar, отображает собственное предупреждение кнопки. - Поскольку всплытие было остановлено, обработчик
<div>родительскогоonClickневыполняется.
В результатеe.stopPropagation()клик по кнопкам теперь показывает только одно предупреждение (от<button>), а не два (от<button>и от родительского<div>панели инструментов). Клик по кнопке — это не то же самое, что клик по окружающей панели инструментов, поэтому остановка всплытия имеет смысл для этого интерфейса.
Передача обработчиков как альтернатива всплытию
Обратите внимание, как этот обработчик клика выполняет строку кодаи затемвызывает пропсonClick, переданный родителем:
Вы также можете добавить больше кода в этот обработчик перед вызовом родительского обработчика событияonClick. Этот шаблон предоставляетальтернативувсплытию. Он позволяет дочернему компоненту обрабатывать событие, а также позволяет родительскому компоненту определять дополнительное поведение. В отличие от всплытия, это не происходит автоматически. Но преимущество этого шаблона в том, что вы можете чётко проследить всю цепочку кода, которая выполняется в результате какого-либо события.
Если вы полагаетесь на всплытие и вам трудно отследить, какие обработчики выполняются и почему, попробуйте вместо этого этот подход.
Предотвращение поведения по умолчанию
Некоторые события браузера имеют связанное с ними поведение по умолчанию. Например, событие отправки<form>, которое происходит при клике на кнопку внутри него, по умолчанию перезагрузит всю страницу:
Вы можете вызватьe.preventDefault()для объекта события, чтобы предотвратить это:
Не путайтеe.stopPropagation() и e.preventDefault(). Оба метода полезны, но они не связаны между собой:
- e.stopPropagation()останавливает срабатывание обработчиков событий, прикрепленных к тегам выше.
- e.preventDefault()предотвращает поведение браузера по умолчанию для тех немногих событий, у которых оно есть.
Могут ли обработчики событий иметь побочные эффекты?
Абсолютно! Обработчики событий — лучшее место для побочных эффектов.
В отличие от функций рендеринга, обработчики событий не обязаны бытьчистыми, поэтому это отличное место, чтобыизменитьчто-то — например, изменить значение поля ввода в ответ на ввод текста или изменить список в ответ на нажатие кнопки. Однако, чтобы изменить какую-либо информацию, сначала нужен способ её хранить. В React это делается с помощьюсостояния, памяти компонента.Вы узнаете всё об этом на следующей странице.
Резюме
- Вы можете обрабатывать события, передавая функцию как пропс элементу, например
<button>. - Обработчики событий должны передаваться,а не вызываться!
onClick={handleClick}, а неonClick={handleClick()}. - Вы можете определить функцию обработчика события отдельно или встроенно.
- Обработчики событий определяются внутри компонента, поэтому они имеют доступ к пропсам.
- Вы можете объявить обработчик события в родительском компоненте и передать его как пропс дочернему.
- Вы можете определять собственные пропсы обработчиков событий с именами, специфичными для приложения.
- События всплывают вверх. Вызовите
e.stopPropagation()для первого аргумента, чтобы предотвратить это. - События могут иметь нежелательное поведение браузера по умолчанию. Вызовите
e.preventDefault(), чтобы предотвратить это. - Явный вызов пропса обработчика события из дочернего обработчика является хорошей альтернативой всплытию.
Попробуйте выполнить несколько заданий
Challenge 1 of 2:Исправьте обработчик события #
При нажатии на эту кнопку фон страницы должен переключаться между белым и чёрным. Однако при нажатии ничего не происходит. Исправьте проблему. (Не беспокойтесь о логике внутри handleClick—эта часть в порядке.)
