Реагирование на события
React позволяет добавлять обработчики событий в ваш JSX. Обработчики событий — это ваши собственные функции, которые будут вызываться в ответ на взаимодействия, такие как нажатие, наведение курсора, фокусировка элементов формы и так далее.
Вы узнаете
- Различные способы написания обработчика событий
- Как передавать логику обработки событий от родительского компонента
- Как события распространяются и как их остановить
Добавление обработчиков событий
Для добавления обработчика события вы сначала определите функцию, а затем передадите её в качестве свойства соответствующему тегу JSX. Например, вот кнопка, которая пока ничего не делает:
export default function Button() { return ( <button> I don't do anything </button> ); }
Вы можете сделать так, чтобы она отображала сообщение при нажатии пользователем, выполнив следующие три шага:
- Объявите функцию с именем
handleClickвнутри вашегоButtonкомпонента. - Реализуйте логику внутри этой функции (используйте
alertдля отображения сообщения). - Добавьте
onClick={handleClick}к<button>JSX.
export default function Button() { function handleClick() { alert('You clicked me!'); } return ( <button onClick={handleClick}> Click me </button> ); }
Вы определили функцию handleClick и затем передали её в качестве свойства к <button>. handleClick является обработчиком события. Функции обработчиков событий:
- Обычно определяются внутри ваших компонентов.
- Имеют имена, начинающиеся с
handle, за которым следует название события.
По соглашению, обработчики событий часто именуются handle с последующим названием события. Вы часто будете видеть onClick={handleClick}, onMouseEnter={handleMouseEnter}, и так далее.
В качестве альтернативы, вы можете определить обработчик события внутри JSX:
<button onClick={function handleClick() {
alert('You clicked me!');
}}> Или, более кратко, с помощью стрелочной функции:
<button onClick={() => {
alert('You clicked me!');
}}> Все эти стили эквивалентны. Встроенные обработчики событий удобны для коротких функций.
Опасная ситуация
Функции, передаваемые обработчикам событий, должны передаваться, а не вызываться. Например:
| передача функции (правильно) | вызов функции (неправильно) |
|---|---|
<button onClick={handleClick}> |
<button onClick={handleClick()}> |
Разница тонкая. В первом примере, функция handleClick передаётся как onClick обработчик события. Это сообщает React запомнить её и вызвать только тогда, когда пользователь нажмёт кнопку.
Во втором примере, () в конце handleClick() вызывает функцию немедленно во время рендеринга, без каких-либо нажатий. Это происходит потому, что JavaScript внутри JSX { и } выполняется сразу.
Когда вы пишете код встроенным способом, та же опасная ситуация проявляется по-другому:
| передача функции (правильно) | вызов функции (неправильно) |
|---|---|
<button onClick={() => alert('...')}> |
<button onClick={alert('...')}> |
Передача такого встроенного кода не сработает при нажатии — он выполняется каждый раз при рендеринге компонента:
// This alert fires when the component renders, not when clicked!
<button onClick={alert('You clicked me!')}>
Если вы хотите определить свой обработчик события встроенным способом, оберните его в анонимную функцию, как показано ниже:
<button onClick={() => alert('You clicked me!')}>
Вместо выполнения кода внутри при каждом рендеринге, это создаёт функцию, которая будет вызвана позже.
В обоих случаях, вы хотите передать функцию:
-
<button onClick={handleClick}>передаёт функциюhandleClick. -
<button onClick={() => alert('...')}>передаёт функцию() => alert('...').
Чтение свойств в обработчиках событий
Поскольку обработчики событий объявляются внутри компонента, они имеют доступ к свойствам компонента. Вот кнопка, которая при нажатии отображает всплывающее окно с её свойством message.
function AlertButton({ message, children }) { return ( <button onClick={() => alert(message)}> {children} </button> ); } export default function Toolbar() { return ( <div> <AlertButton message="Playing!"> Play Movie </AlertButton> <AlertButton message="Uploading!"> Upload Image </AlertButton> </div> ); }
Это позволяет этим двум кнопкам отображать разные сообщения. Попробуйте изменить сообщения, переданные им.
Передача обработчиков событий в качестве свойств
Часто вы хотите, чтобы родительский компонент определял обработчик события дочернего компонента. Рассмотрим кнопки: в зависимости от того, где вы используете компонент Button, вы можете захотеть выполнить другую функцию — может быть, одну, которая воспроизводит фильм, а другую, которая загружает изображение.
Для этого передайте свойство, которое компонент получает от родителя, в качестве обработчика события, как показано ниже:
function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } function PlayButton({ movieName }) { function handlePlayClick() { alert(`Playing ${movieName}!`); } return ( <Button onClick={handlePlayClick}> Play "{movieName}" </Button> ); } function UploadButton() { return ( <Button onClick={() => alert('Uploading!')}> Upload Image </Button> ); } export default function Toolbar() { return ( <div> <PlayButton movieName="Kiki's Delivery Service" /> <UploadButton /> </div> ); }
Здесь, компонент 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:
function Button({ onSmash, children }) { return ( <button onClick={onSmash}> {children} </button> ); } export default function App() { return ( <div> <Button onSmash={() => alert('Playing!')}> Play Movie </Button> <Button onSmash={() => alert('Uploading!')}> Upload Image </Button> </div> ); }
В этом примере, <button onClick={onSmash}> показывает, что браузерному <button> (строчные буквы) всё ещё необходимо свойство, называемое onClick, но имя свойства, получаемого вашим настраиваемым компонентом Button, зависит от вас!
Когда ваш компонент поддерживает несколько взаимодействий, вы можете давать именам свойств обработчиков событий названия, соответствующие концепциям приложения. Например, этот компонент Toolbar получает обработчики событий onPlayMovie и onUploadImage:
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Playing!')} onUploadImage={() => alert('Uploading!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Play Movie </Button> <Button onClick={onUploadImage}> Upload Image </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Заметьте, как компонент App не должен знать, что Toolbar будет делать с onPlayMovie или onUploadImage. Это деталь реализации компонента Toolbar. Здесь, Toolbar передаёт их как обработчики onClick своим Button, но позже он также может инициировать их при нажатии клавиши. Использование имён свойств, соответствующих взаимодействиям приложения, таких как onPlayMovie, даёт вам гибкость в изменении их использования позже.
Примечание
Убедитесь, что вы используете соответствующие теги HTML для ваших обработчиков событий. Например, для обработки нажатий используйте <button onClick={handleClick}> вместо <div onClick={handleClick}>. Использование реального браузерного <button> включит встроенное поведение браузера, такое как навигация по клавиатуре. Если вам не нравится стандартный стиль браузера кнопки и вы хотите сделать его более похожим на ссылку или другой элемент пользовательского интерфейса, вы можете сделать это с помощью CSS. Узнайте больше о написании доступного разметки.
Распространение событий
Обработчики событий также будут улавливать события от любых дочерних элементов, которые может иметь ваш компонент. Мы говорим, что событие «всплывает» или «распространяется» вверх по дереву: оно начинается с места, где произошло событие, а затем поднимается по дереву.
Этот <div> содержит две кнопки. У <div> и каждой кнопки есть свои обработчики onClick. Какие обработчики, по вашему мнению, сработают при нажатии кнопки?
export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('You clicked on the toolbar!'); }}> <button onClick={() => alert('Playing!')}> Play Movie </button> <button onClick={() => alert('Uploading!')}> Upload Image </button> </div> ); }
Если вы нажмёте любую кнопку, её обработчик onClick будет запущен первым, за которым последует обработчик родительского <div>’s onClick. Таким образом, появятся два сообщения. Если вы нажмёте на сам инструмент, запустится только обработчик родительского <div>’s onClick.
Опасная ситуация
Все события распространяются в React, за исключением onScroll, которое работает только на теге JSX, к которому оно прикреплено.
Остановка распространения
Обработчики событий получают объект события в качестве единственного аргумента. По соглашению, он обычно называется e, что означает «событие». Вы можете использовать этот объект для чтения информации о событии.
Этот объект события также позволяет остановить распространение. Если вы хотите предотвратить достижение события родительскими компонентами, вам нужно вызвать e.stopPropagation() так, как это делает компонент Button:
function Button({ onClick, children }) { return ( <button onClick={e => { e.stopPropagation(); onClick(); }}> {children} </button> ); } export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('You clicked on the toolbar!'); }}> <Button onClick={() => alert('Playing!')}> Play Movie </Button> <Button onClick={() => alert('Uploading!')}> Upload Image </Button> </div> ); }
Когда вы нажимаете на кнопку:
- React вызывает обработчик
onClickпереданный в<button>. - Этот обработчик, определённый в
Button, выполняет следующее:- Вызывает
e.stopPropagation(), предотвращая дальнейшее распространение события. - Вызывает функцию
onClick, которая является свойством, переданным из компонентаToolbar.
- Вызывает
- Эта функция, определённая в компоненте
Toolbar, отображает собственное всплывающее окно кнопки. - Поскольку распространение было остановлено, обработчик родительского
<div>’sonClickне выполняется.
В результате e.stopPropagation(), нажатие на кнопки теперь отображает только одно всплывающее окно (из <button>) вместо двух (из <button> и родительского инструмента <div>). Нажатие кнопки отличается от нажатия на окружающий инструмент, поэтому остановка распространения имеет смысл для этого пользовательского интерфейса.
Глубокое погружение
События фазы захвата
В редких случаях вам может потребоваться поймать все события в дочерних элементах, даже если они остановили распространение. Например, может быть необходимо записать каждое нажатие в аналитику, независимо от логики распространения. Вы можете сделать это, добавив Capture в конце имени события:
<div onClickCapture={() => { /* this runs first */ }}>
<button onClick={e => e.stopPropagation()} />
<button onClick={e => e.stopPropagation()} />
</div>
Каждое событие распространяется в трёх фазах:
- Оно проходит вниз, вызывая все обработчики
onClickCapture. - Выполняется обработчик кликаемого элемента
onClick. - Оно распространяется вверх, вызывая все обработчики
onClick.
События захвата полезны для кода маршрутизаторов или аналитики, но, вероятно, вы не будете использовать их в коде приложения.
Передача обработчиков как альтернатива распространению
Обратите внимание, как этот обработчик клика выполняет строку кода и затем вызывает свойство onClick переданное родителем:
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
} Вы можете добавить больше кода в этот обработчик перед вызовом родительского обработчика события onClick тоже. Эта схема предоставляет альтернативу распространению. Она позволяет дочернему компоненту обрабатывать событие, а также позволяет родительскому компоненту указать некоторое дополнительное поведение. В отличие от распространения, это не происходит автоматически. Но преимущество этой схемы заключается в том, что вы можете четко отследить всю цепочку кода, который выполняется в результате какого-либо события.
Если вы полагаетесь на распространение, и трудно отследить, какие обработчики выполняются и почему, попробуйте вместо этого этот подход.
Предотвращение стандартного поведения
Некоторые события браузера имеют связанное с ними стандартное поведение. Например, событие <form> submit, которое происходит при нажатии кнопки внутри него, по умолчанию перезагрузит всю страницу:
export default function Signup() { return ( <form onSubmit={() => alert('Submitting!')}> <input /> <button>Send</button> </form> ); }
Вы можете вызвать e.preventDefault() на объекте события, чтобы остановить это:
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Submitting!'); }}> <input /> <button>Send</button> </form> ); }
Не путайте e.stopPropagation() и e.preventDefault(). Они оба полезны, но не связаны:
-
e.stopPropagation()останавливает срабатывание обработчиков событий, прикрепленных к тегам выше. -
e.preventDefault()предотвращает стандартное поведение браузера для нескольких событий, которые его имеют.
Могут ли обработчики событий иметь побочные эффекты?
Конечно! Обработчики событий — лучшее место для побочных эффектов.
В отличие от функций отрисовки, обработчики событий не должны быть чистыми, поэтому это отличное место для изменения чего-либо — например, изменения значения поля ввода в ответ на ввод текста или изменения списка в ответ на нажатие кнопки. Однако, чтобы изменить некоторую информацию, вам сначала нужно каким-то образом ее хранить. В React это делается с помощью состояния, памяти компонента. Вы узнаете все об этом на следующей странице.
Резюме
- Вы можете обрабатывать события, передавая функцию в качестве свойства элементу, как
<button>. - Обработчики событий должны передаваться, а не вызываться!
onClick={handleClick}, а неonClick={handleClick()}. - Вы можете определить функцию обработчика события отдельно или в строку.
- Обработчики событий определяются внутри компонента, поэтому они могут получить доступ к свойствам.
- Вы можете объявить обработчик события в родительском элементе и передать его как свойство дочернему элементу.
- Вы можете определить свои собственные свойства обработчиков событий с именами, специфичными для приложения.
- События распространяются вверх. Вызовите
e.stopPropagation()в первом аргументе, чтобы предотвратить это. - События могут иметь нежелательное стандартное поведение браузера. Вызовите
e.preventDefault(), чтобы предотвратить это. - Явное вызов свойства обработчика события из обработчика дочернего компонента — хороший альтернативный способ обработке распространения.
Попробуйте выполнить некоторые задачи
Задача 1 из 2:
Исправить обработчик события
Нажатие этой кнопки должно переключать цвет фона страницы между белым и черным. Однако ничего не происходит при нажатии. Исправьте проблему. (Не беспокойтесь о логике внутри handleClick — эта часть в порядке.)
export default function LightSwitch() { function handleClick() { let bodyStyle = document.body.style; if (bodyStyle.backgroundColor === 'black') { bodyStyle.backgroundColor = 'white'; } else { bodyStyle.backgroundColor = 'black'; } } return ( <button onClick={handleClick()}> Toggle the lights </button> ); }
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://18.react.dev/learn/responding-to-events