Odgovaranje na event-e
React vam omogućava da dodate event handler-e u vaš JSX. Event handler-i su vaše sopstvene funkcije koje će se pokrenuti kao odgovor na korisničke interakcije poput klika, prelaženja mišem, fokusiranja na input-e forme i tako dalje.
Naučićete:
- Različite načine da napišete event handler
- Kako proslediti logiku za obrađivanje event-ova iz roditeljske komponente
- Kako se event-i propagiraju i kako ih zaustaviti
Dodavanje event handler-a
Da biste dodali event handler, prvo ćete definisati funkciju, a onda je proslediti kao prop u odgovarajući JSX tag. Na primer, ovde je dugme koje još uvek ništa ne radi:
export default function Button() { return ( <button> Ne radim ništa </button> ); }
Možete učiniti da se prikaže poruka kad ga korisnik klikne prateći ova tri koraka:
- Deklarišite funkciju pod imenom
handleClick
unutarButton
komponente. - Implementirajte logiku unutar te funkcije (koristite
alert
da prikažete poruku). - Dodajte
onClick={handleClick}
u<button>
JSX.
export default function Button() { function handleClick() { alert('Kliknuli ste me!'); } return ( <button onClick={handleClick}> Klikni me </button> ); }
Definisali ste handleClick
funkciju i prosledili ste je kao prop u <button>
. handleClick
je event handler. Event handler funkcije:
- Su uglavnom definisane unutar vaših komponenata.
- Imaju imena koja počinju sa
handle
, nakon čega sledi ime event-a.
Po konvenciji, praksa je da imena event handler-a počinju sa handle
, nakon čega sledi ime event-a. Često ćete videti onClick={handleClick}
, onMouseEnter={handleMouseEnter}
i slično.
Alternativno, možete definisati event handler inline u JSX-u:
<button onClick={function handleClick() {
alert('Kliknuli ste me!');
}}>
Ili konciznije, upotrebom arrow funkcije:
<button onClick={() => {
alert('Kliknuli ste me!');
}}>
Svi ovi načini su ekvivalentni. Inline event handler-i su zgodni za kratke funkcije.
Čitanje props-a u event handler-ima
Pošto su event handler-i deklarisani unutar komponente, imaju pristup props-ima komponente. Ovde je dugme koje, kad je kliknuto, prikazuje alert sa message
prop-om:
function AlertButton({ message, children }) { return ( <button onClick={() => alert(message)}> {children} </button> ); } export default function Toolbar() { return ( <div> <AlertButton message="Puštanje!"> Pusti film </AlertButton> <AlertButton message="Upload-ovanje!"> Upload-uj sliku </AlertButton> </div> ); }
Ovo omogućava tim dugmićima da prikažu različite poruke. Probajte da promenite poruke koje su im prosleđene.
Prosleđivanje event handler-a kao prop-a
Često ćete želeti da roditeljska komponenta specificira dečji event handler. Razmotrite dugmiće: u zavisnosti od toga gde koristite Button
komponentu, možete poželeti da izvršite različite funkcije—jedna da pušta film, druga da upload-uje sliku.
Da biste to uradili, prosledite prop koji komponenta primi od roditelja kao event handler:
function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } function PlayButton({ movieName }) { function handlePlayClick() { alert(`Puštanje ${movieName}!`); } return ( <Button onClick={handlePlayClick}> Pusti "{movieName}" </Button> ); } function UploadButton() { return ( <Button onClick={() => alert('Upload-ovanje!')}> Upload-uj sliku </Button> ); } export default function Toolbar() { return ( <div> <PlayButton movieName="Kikina služba za dostavu" /> <UploadButton /> </div> ); }
Ovde, Toolbar
komponenta renderuje PlayButton
i UploadButton
:
PlayButton
prosleđujehandlePlayClick
kaoonClick
prop uButton
komponentu.UploadButton
prosleđuje() => alert('Upload-ovanje!')
kaoonClick
prop uButton
komponentu.
Konačno, vaša Button
komponenta prima prop po imenu onClick
. Taj prop direktno prosleđuje u ugrađeni <button>
tag sa onClick={onClick}
. Ovo govori React-u da pozove prosleđenu funkciju na klik.
Ako koristite sistem dizajna, uobičajeno je da komponente poput dugmića sadrže stajling, ali da ne specificiraju ponašanje. Umesto toga, komponente poput PlayButton
i UploadButton
će im proslediti event handler-e.
Imenovanje event handler props-a
Ugrađene komponente kao što su <button>
i <div>
podržavaju samo imena event-ova u pretraživaču poput onClick
. Međutim, kada pravite sopstvene komponente, vaše event handler props-e možete imenovati kako god želite.
Po konvenciji, event handler props-i trebaju započeti sa on
, nakon čega sledi veliko slovo.
Na primer, onClick
prop u Button
komponenti može biti nazvan onSmash
:
function Button({ onSmash, children }) { return ( <button onClick={onSmash}> {children} </button> ); } export default function App() { return ( <div> <Button onSmash={() => alert('Puštanje!')}> Pusti film </Button> <Button onSmash={() => alert('Upload-ovanje!')}> Upload-uj sliku </Button> </div> ); }
U ovom primeru, <button onClick={onSmash}>
prikazuje da ugrađeni <button>
(malim slovima) i dalje zahteva prop sa imenom onClick
, ali ime prop-a koji prima vaša custom Button
komponenta je na vama!
Kada komponenta podržava više interakcija, možete imenovati event handler props-e na osnovu koncepata specifičnih za aplikaciju. Na primer, ova Toolbar
komponenta prima onPlayMovie
i onUploadImage
event handler-e:
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Puštanje!')} onUploadImage={() => alert('Upload-ovanje!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Pusti film </Button> <Button onClick={onUploadImage}> Upload-uj sliku </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Primetite da App
komponenta ne mora da zna šta će Toolbar
uraditi sa onPlayMovie
i onUploadImage
. To je implementacijski detalj u Toolbar
-u. Takođe, Toolbar
ih prosleđuje kao onClick
handler-e u svoje Button
-e, ali ih kasnije može okinuti i za prečicu na tastaturi. Imenovanje event handler props-a na osnovu koncepata specifičnih za aplikaciju poput onPlayMovie
vam daje fleksibilnost da kasnije menjate kako se koriste.
Propagacija event-ova
Event handler-i će takođe uhvatiti event-e iz svake dečje komponente. Kažemo da se event “ponaša kao mehurić” ili “propagira” uz stablo: počinje tamo gde se event desio, a onda kreće uz stablo.
Ovaj <div>
sadrži dva dugmeta. I <div>
, ali i svako dugme, imaju svoj onClick
handler. Šta mislite koji handler-i će se okinuti kada kliknete na dugme?
export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('Kliknuli ste na toolbar!'); }}> <button onClick={() => alert('Puštanje!')}> Pusti film </button> <button onClick={() => alert('Upload-ovanje!')}> Upload-uj sliku </button> </div> ); }
Ako kliknete na neko dugme, prvo će se izvršiti njegov onClick
, a nakon toga i onClick
od roditeljskog <div>
-a. Znači, dve poruke će se prikazati. Ako kliknete samo na toolbar, samo će se izvršiti onClick
na <div>
-u.
Zaustavljanje propagacije
Event handler-i primaju event objekat kao jedini argument. Po konvenciji, često se naziva e
, što označava “event”. Možete koristiti ovaj objekat da čitate informacije o event-u.
Ovaj event objekat vam takođe omogućava da zaustavite propagaciju. Ako želite da sprečite event da dospe do roditeljskih komponenata, morate pozvati e.stopPropagation()
kao što to radi Button
komponenta:
function Button({ onClick, children }) { return ( <button onClick={e => { e.stopPropagation(); onClick(); }}> {children} </button> ); } export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('Kliknuli ste na toolbar!'); }}> <Button onClick={() => alert('Puštanje!')}> Pusti film </Button> <Button onClick={() => alert('Upload-ovanje!')}> Upload-uj sliku </Button> </div> ); }
Kada kliknete na dugme:
- React poziva
onClick
handler prosleđen u<button>
. - Taj handler, definisan u
Button
-u, radi sledeće:- Poziva
e.stopPropagation()
, sprečavajući da se event propagira dalje. - Poziva
onClick
funkciju, koja je prop prosleđen izToolbar
komponente.
- Poziva
- Ta funkcija, definisana u
Toolbar
komponenti, prikazuje poruku specifičnu za dugme. - Pošto je propagacija zaustavljena,
onClick
handler u roditeljskom<div>
-u se ne pokreće.
Kao rezultat od e.stopPropagation()
, klik na dugmiće prikazuje samo jednu poruku (iz <button>
-a) umesto dve (iz <button>
-a i iz roditeljskog toolbar <div>
-a). Kliktanje dugmeta nije isto kao kliktanje okružujućeg toolbar-a, tako da zaustavljanje propagacije ima smisla za ovaj UI.
Deep Dive
U retkim slučajevima, možete poželeti da uhvatite sve event-e iz dečjih elemenata, čak iako su zaustavili propagaciju. Na primer, želite da logujete svaki klik za analitiku, nevezano sa logikom propagacije. To možete uraditi dodavanjem Capture
na kraju imena event-a:
<div onClickCapture={() => { /* ovo se izvršava prvo */ }}>
<button onClick={e => e.stopPropagation()} />
<button onClick={e => e.stopPropagation()} />
</div>
Svaki event se propagira u tri faze:
- Putuje na dole, pozivajući sve
onClickCapture
handler-e. - Pokreće
onClick
handler na kliknutom elementu. - Putuje na gore, pozivajući sve
onClick
handler-e.
Capture event-i su korisni za rutere i analitiku, ali ih verovatno nećete koristiti u kodu aplikacije.
Prosleđivanje handler-a kao alternative za propagaciju
Primetite da ovaj klik handler pokreće liniju koda, a onda poziva onClick
prop prosleđen iz roditelja:
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
Takođe, možete dodati još koda u ovaj handler pre poziva roditeljskog onClick
event handler-a. Ovaj šablon pruža alternativu propagaciji. Omogućava dečjoj komponenti da obradi event, dopuštajući roditeljskoj komponenti da specificira i neko dodatno ponašanje. Za razliku od propagacije, ovo nije automatsko. Međutim, benefit ovog šablona je da jasno možete ispratiti sav kod koji se izvršava kao rezultat nekog event-a.
Ako se oslanjate na propagaciju i teško vam je da ispratite koji handler-i se izvršavaju i zašto, probajte ovaj pristup.
Sprečavanje default ponašanja
Neki event-i u pretraživačima imaju default ponašanje. Na primer, <form>
submit event, koji se okida kada se klikne dugme unutar forme, ponovo će učitati celu stranicu po default-u:
export default function Signup() { return ( <form onSubmit={() => alert('Submit-ovanje!')}> <input /> <button>Pošalji</button> </form> ); }
Možete pozvati e.preventDefault()
nad event objektom kako biste zaustavili ovo:
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Submit-ovanje!'); }}> <input /> <button>Pošalji</button> </form> ); }
Nemojte pomešati e.stopPropagation()
i e.preventDefault()
. Korisni su, ali nisu povezani:
e.stopPropagation()
zaustavlja okidanje event handler-a povezanih sa tag-ovima iznad u hijerarhiji.e.preventDefault()
sprečava default ponašanje pretraživača za par event-ova koji ga imaju.
Da li event handler-i smeju imati propratne efekte?
Apsolutno! Event handler-i su najbolje mesto za propratne efekte.
Za razliku od funkcija za renderovanje, event handler-i ne moraju biti čisti, tako da su odlično mesto za promenu nečega—na primer, promena input vrednosti kao rezultat kucanja, ili promena liste kao odgovor na klik dugmeta. Međutim, da biste izmenili neku informaciju, prvo vam je potreban način da je čuvate. U React-u, to se dešava kroz state, memoriju komponente. Naučićete sve o tome na narednoj stranici.
Recap
- Možete obrađivati event-e prosleđivanjem funkcije kao prop-a u elemente poput
<button>
-a. - Event handler-i se moraju proslediti, a ne pozvati!
onClick={handleClick}
, neonClick={handleClick()}
. - Možete definisati event handler funkciju zasebno ili inline.
- Event handler-i su definisani unutar komponente kako bi mogli pristupiti props-ima.
- Možete deklarisati event handler u roditelju i proslediti ga kao prop detetu.
- Možete definisati sopstvene event handler props-e sa imenima na osnovu koncepata specifičnih za aplikaciju.
- Event-i se propagiraju nagore. Pozovite
e.stopPropagation()
nad prvim argumentom kako biste to sprečili. - Event-i mogu imati neželjeno default ponašanje u pretraživaču. Pozovite
e.preventDefault()
da to sprečite. - Eksplicitno pozivanje event handler prop-a iz dečjeg handler-a je dobra alternativa za propagaciju.
Izazov 1 od 2: Popraviti event handler
Klikom na dugme bi se trebala promeniti pozadina stranice između bele i crne. Međutim, kad kliknete ništa se ne dešava. Popravite problem. (Ne brinite za logiku unutar handleClick
—taj deo je u redu.)
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()}> Promeni pozadinu </button> ); }