Рэагаванне на падзеі
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> ); }