Рэагаванне на падзеі
React дазваляе дадаваць апрацоўшчыкі падзей у JSX. Апрацоўшчыкі падзей — гэта вашыя ўласныя функцыі, якія будуць запускацца ў адказ на ўзаемадзеянне карыстальніка, напрыклад, націсканне кнопак, навядзенне курсора на элементы, атрыманне фокусу на ўводах формы і гэтак далей.
You will learn
- Розныя спосабы напісання апрацоўшчыкаў падзей
- Як перадаць логіку апрацоўкі падзей ад бацькоўскіх кампанентаў
- Як падзеі распаўсюджваюцца і як іх спыніць
Дадаванне апрацоўшчыкаў падзей
Каб дадаць апрацоўшчык падзеі, спачатку трэба вызначыць функцыю, а потым перадаць яе ў якасці пропса ў адпаведны JSX тэг. Напрыклад, вось кнопка, якая пакуль нічога не робіць:
export default function Button() { return ( <button> Я нічога не раблю </button> ); }
Для таго, каб пры націсканні на кнопку паказвалася паведамленне, выканайце наступныя тры крокі:
- Аб’явіце функцыю
handleClick
унутры кампанентаButton
. - Рэалізуйце логіку ўнутры гэтай функцыі (выкарыстоўвайце
alert
, каб паказаць паведамленне). - Дадайце
onClick={handleClick}
у JSX тэг<button>
.
export default function Button() { function handleClick() { alert('Вы націснулі на мяне!'); } return ( <button onClick={handleClick}> Націсніце на мяне </button> ); }
Вы аб’явілі функцыю handleClick
і затым перадалі яе ў якасці пропса ў <button>
. Функцыя handleClick
— гэта апрацоўшчык падзей. Функцыі апрацоўшчыкі падзей:
- Звычайна аб’яўляюцца ўнутры вашых кампанентаў.
- Маюць назвы, якія пачынаецца са слова
handle
, за якім ідзе назва падзеі.
Назвы апрацоўшчыкаў падзей звычайна пачынаюцца са слова handle
, за якім ідзе назва падзеі. Вы часта будзеце сустракаць onClick={handleClick}
, onMouseEnter={handleMouseEnter}
і т.п.
У якасці альтэрнатывы, вы можаце аб’явіць апрацоўшчык падзей унутры JSX:
<button onClick={function handleClick() {
alert('Вы націснулі на мяне!');
}}>
Або, больш сцісла, выкарыстоўваючы стрэлачную функцыю:
<button onClick={() => {
alert('Вы націснулі на мяне!');
}}>
Абодва гэтыя варыянты эквівалентныя. Апрацоўшчыкі падзей унутры JSX зручныя для кароткіх функцый.
Чытанне пропсаў у апрацоўшчыках падзей
Паколькі апрацоўшчыкі падзей аб’яўлены ўнутры кампанента, яны маюць доступ да пропсаў кампанента. Вось кнопка, пры націсканні на якую паказваецца паведамленне са значэннем пропса message
:
function AlertButton({ message, children }) { return ( <button onClick={() => alert(message)}> {children} </button> ); } export default function Toolbar() { return ( <div> <AlertButton message="Прайграваецца!"> Прайграць фільм </AlertButton> <AlertButton message="Запампоўваецца!"> Запампаваць відарыс </AlertButton> </div> ); }
Дзякуючы гэтаму дзве кнопкі паказваюць розныя паведамленні. Паспрабуйце змяніць значэнні, якія ім перадаюцца.
Перадача апрацоўшчыкаў падзей у якасці пропсаў
Часта вам можа спатрэбіцца, каб бацькоўскі кампанент вызначаў апрацоўшчыка падзей для даччынага кампанента. Разгледзім кнопкі: у залежнасці ад таго, дзе вы выкарыстоўваеце кампанент Button
, вам можа спатрэбіцца выконваць розныя функцыі — магчыма, адна кнопка павінна прайграваць фільм, а другая загружаць відарыс.
Каб зрабіць гэта, перадайце кампаненту пропс, які ён атрымлівае ад свайго бацькі, у якасці апрацоўшчыка падзей, наступным чынам:
function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } function PlayButton({ movieName }) { function handlePlayClick() { alert(`Прайграваецца ${movieName}!`); } return ( <Button onClick={handlePlayClick}> Прайграць "{movieName}" </Button> ); } function UploadButton() { return ( <Button onClick={() => alert('Запампоўваецца!')}> Запампаваць відарыс </Button> ); } export default function Toolbar() { return ( <div> <PlayButton movieName="Ведзьміна дастаўка" /> <UploadButton /> </div> ); }
Тут кампанент Toolbar
рэндэрыць кампаненты PlayButton
і UploadButton
:
PlayButton
перадаеhandlePlayClick
у якасці пропсаonClick
у кампанентButton
.UploadButton
перадае() => alert('Запампоўваецца!')
у якасці пропсаonClick
у кампанентButton
.
Нарэшце, ваш кампанент Button
прымае пропс пад назвай onClick
. Ён перадае гэты пропс непасрэдна ва ўбудаваны браўзерны кампанент <button>
з дапамогай onClick={onClick}
. Гэта загадвае React выклікаць перададзеную функцыю пры націсканні.
Калі вы выкарыстоўваеце дызайн-сістэму, то звычайна такія кампаненты, як кнопкі, змяшчаюць стылі, але не вызначаюць паводзіны. Замест гэтага кампаненты, як PlayButton
і UploadButton
, будуць перадаваць апрацоўшчыкі падзей уніз па іерархіі.
Называнне пропсаў апрацоўшчыка падзей
Убудаваныя кампаненты, такія як <button>
і <div>
, падтрымліваюць толькі назвы падзей браўзера, напрыклад onClick
. Аднак, пры стварэнні ўласных кампанентаў, вы можаце называць іх апрацоўшчыкі падзей так, як вам падабаецца.
Назва пропса апрацоўшчыка падзей павінна пачынацца з on
, за якім ідзе вялікая літара.
Напрыклад, пропс для падзеі onClick
у кампаненце Button
мог бы называцца onSmash
:
function Button({ onSmash, children }) { return ( <button onClick={onSmash}> {children} </button> ); } export default function App() { return ( <div> <Button onSmash={() => alert('Прайграваецца!')}> Прайграць фільм </Button> <Button onSmash={() => alert('Запампоўваецца!')}> Запампаваць відарыс </Button> </div> ); }
У гэтым прыкладзе <button onClick={onSmash}>
паказвае, што браўзерны кампанент <button>
(з маленькай літары) па-ранейшаму патрэбуе пропс з назвай onClick
, але назву пропса, які атрымлівае ваш карыстальніцкі кампанентам Button
, выбіраеце вы!
Калі ваш кампанент падтрымлівае некалькі ўзаемадзеянняў, вы можаце назваць пропсы апрацоўшчыкаў падзей у адпаведнасці з канкрэтнымі канцэпцыямі вашай праграмы. Напрыклад, кампанент Toolbar
атрымлівае апрацоўшчыкі падзей onPlayMovie
і onUploadImage
:
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Прайграваецца!')} onUploadImage={() => alert('Запампоўваецца!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Прайграць фільм </Button> <Button onClick={onUploadImage}> Запампаваць відарыс </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Звярніце ўвагу, што кампанент App
не павінен ведаць, што кампанент Toolbar
будзе рабіць з onPlayMovie
або onUploadImage
. Гэта дэталі рэалізацыі Toolbar
. У гэтым прыкладзе Toolbar
перадае іх у якасці апрацоўшчыкаў падзеі onClick
сваім кампанентам Button
, але пазней ён таксама можа выклікаць іх па спалучэнні клавіш. Называнне пропсаў у адпаведнасці са спецыфікай вашай праграмы, напрыклад onPlayMovie
, дае вам магчымасць змяніць спосаб іх выкарыстання ў будучыні.
Распаўсюджванне падзей
Апрацоўшчыкі падзей таксама будуць перахопліваць падзеі ад любых даччыных кампанентаў. Кажуць, што падзея «ўсплывае» або «распаўсюджваецца» ўверх па дрэве: яна пачынаецца там, дзе адбылася падзея, а потым ідзе ўверх па дрэве.
Гэты <div>
змяшчае дзве кнопкі. У <div>
і ў кожнай кнопкі ёсць уласныя апрацоўшчыкі падзеі onClick
. Як думаеце, якія апрацоўшчыкі будуць выкліканы, калі вы націснеце кнопку?
export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('Вы націснулі на панэль інструментаў!'); }}> <button onClick={() => alert('Прайграваецца!')}> Прайграць фільм </button> <button onClick={() => alert('Запампоўваецца!')}> Запампаваць відарыс </button> </div> ); }
Калі вы націснеце на любую кнопку, то спачатку будзе выкліканы яе апрацоўшчык onClick
, а пасля апрацоўшчык onClick
бацькоўскага элемента <div>
. Такім чынам, з’явяцца два паведамленні. Калі вы націснеце на саму панэль інструментаў, то будзе выкліканы толькі апрацоўшчык onClick
бацькоўскага элемента <div>
.
Спыненне распаўсюджвання падзей
Апрацоўшчыкі падзей атрымліваюць аб’ект падзеі ў якасці адзінага аргумента. Звычайна гэты аб’ект называецца e
, што расшыфроўваецца як «event» (падзея). З дапамогай гэтага аб’екта вы можаце атрымаць інфармацыю аб падзеі.
Аб’ект падзеі таксама дазваляе спыніць распаўсюджванне падзеі. Калі вы не хочаце, каб падзея дасягнула бацькоўскіх кампанентаў, вам трэба выклікаць 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('Вы націснулі на панэль інструментаў!'); }}> <Button onClick={() => alert('Прайграваецца!')}> Прайграць фільм </Button> <Button onClick={() => alert('Запампоўваецца!')}> Запампаваць відарыс </Button> </div> ); }
Пры націсканні на кнопку:
- React выклікае апрацоўшчык
onClick
, перададзены ў<button>
. - Гэты апрацоўшчык, аб’яўлены ў
Button
, выконвае наступнае:- Выклікае
e.stopPropagation()
, што прадухіляе далейшае ўсплыванне падзеі. - Выклікае функцыю
onClick
, якая з’яўляецца пропсам, які перадалі з кампанентаToolbar
.
- Выклікае
- Гэтая функцыя, аб’яўленая ў кампаненце
Toolbar
, паказвае ўсплывальнае акноalert
для кнопкі. - Паколькі распаўсюджванне падзеі было спынена, апрацоўшчык
onClick
бацькоўскага элемента<div>
не выклікаецца.
У выніку e.stopPropagation()
, пры націсканні на кнопкі цяпер паказваецца толькі адно ўсплывальнае акно (ад <button>
), а не два (ад <button>
і ад бацькоўскага <div>
у кампаненце Toolbar
). Націсканне кнопкі — гэта не тое ж самае, што націсканне на панэль інструментаў, таму спыненне распаўсюджвання падзеі мае сэнс для гэтага інтэрфейсу.
Deep Dive
У рэдкіх выпадках вам можа спатрэбіцца перахапіць усе падзеі на даччыных элементах, нават калі яны спынілі распаўсюджванне. Напрыклад, магчыма, вы хочаце зарэгістраваць кожнае націсканне мышы ў аналітыцы, незалежна ад логікі распаўсюджвання. Для гэтага можна дадаць Capture
у канец назвы падзеі:
<div onClickCapture={() => { /* гэта адбываецца ў першую чаргу */ }}>
<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>
, якая адбываецца пры націсканні кнопкі ўнутры яе, прадвызначана перазагружае ўсю старонку:
export default function Signup() { return ( <form onSubmit={() => alert('Адпраўка!')}> <input /> <button>Адправіць</button> </form> ); }
Вы можаце выклікаць e.preventDefault()
для аб’екта падзеі, каб прадухіліць гэта:
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Адпраўка!'); }}> <input /> <button>Адправіць</button> </form> ); }
Не блытайце функцыі e.stopPropagation()
і e.preventDefault()
. Яны абедзве карысныя, але не звязаны паміж сабой:
e.stopPropagation()
спыняе спрацоўванне апрацоўшчыкаў падзей, прывязаных да тэгаў вышэй па іерархіі DOM.e.preventDefault()
прадухіляе прадвызначаныя паводзіны браўзера для некаторых падзей.
Ці могуць апрацоўшчыкі падзей мець пабочныя эфекты?
Вядома! Апрацоўшчыкі падзей — гэта лепшае месца для пабочных эфектаў.
У адрозненне ад функцый рэндэрынгу, апрацоўшчыкі падзей не павінны быць чыстымі функцыямі, таму гэта выдатнае месца, каб змяніць што-небудзь — напрыклад, змяніць значэнне ўводу ў адказ на набор тэксту або змяніць спіс у адказ на націсканне кнопкі. Аднак, каб змяніць нейкую інфармацыю, спачатку яе трэба неяк захаваць. У React для гэтага выкарыстоўваецца стан — памяць кампанента. Пра ўсё гэта вы даведаецеся на наступнай старонцы.
Recap
- Вы можаце апрацоўваць падзеі, перадаючы функцыю ў якасці пропса элементу, напрыклад
<button>
. - Апрацоўшчыкі падзей павінны перадавацца, а не выклікацца! Трэба
onClick={handleClick}
, а неonClick={handleClick()}
. - Вы можаце аб’явіць функцыю апрацоўшчыка падзей асобна або ў самім тэгу (убудаваным чынам).
- Апрацоўшчыкі падзей аб’яўляюцца ўнутры кампанента, таму яны маюць доступ да пропсаў.
- Вы можаце аб’явіць апрацоўшчык падзей у бацькоўскім кампаненце і перадаць яго ў якасці пропса даччынаму кампаненту.
- Вы можаце аб’яўляць свае ўласныя пропсы апрацоўшчыка падзей з назвамі, спецыфічнымі для вашай праграмы.
- Падзеі распаўсюджваюцца ўверх. Каб прадухіліць гэта, выклічце
e.stopPropagation()
. - Падзеі могуць мець непажаданыя прадвызначаныя паводзіны браўзера. Каб прадухіліць гэта, выклічце
e.preventDefault()
. - Яўны выклік апрацоўшчыка падзей з даччынага апрацоўшчыка з’яўляецца добрай альтэрнатывай распаўсюджванню падзеі.
Challenge 1 of 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()}> Пераключыць тэму </button> ); }