Rispondere agli Eventi
React ti permette di aggiungere dei gestori di eventi al tuo JSX. I gestori di eventi sono funzioni scritte da te che verranno attivate in risposta ad interazioni come il click, il passaggio del mouse, il focus sugli input dei form, e così via.
Imparerai
- Modi differenti per scrivere un gestore di eventi
- Come passare la logica di gestione degli eventi da un componente genitore
- Come si propagano gli eventi e come fermarli
Aggiungere gestori di eventi
Per aggiungere un gestore di eventi, dovrai prima definire una funzione e poi passarla come prop al tag JSX appropriato. Ad esempio, ecco un bottone che per il momento non fa nulla:
export default function Button() { return ( <button> I don't do anything </button> ); }
Puoi fargli mostrare un messaggio quando un utente clicca seguendo questi tre passaggi:
- Dichiara una funzione chiamata
handleClick
all’interno del tuo componenteButton
. - Implementa la logica all’interno di quella funzione (usa
alert
per mostrare il messaggio). - Aggiungi
onClick={handleClick}
al tuo JSX<button>
.
export default function Button() { function handleClick() { alert('You clicked me!'); } return ( <button onClick={handleClick}> Click me </button> ); }
Hai definito la funzione handleClick
e poi l’hai passata come prop al tag <button>
. handleClick
è un gestore di eventi. Le funzioni gestore di eventi:
- Sono solitamente definite all’interno dei tuoi componenti.
- Hanno nomi che iniziano con
handle
, seguiti dal nome dell’evento.
Per convenzione, è comune chiamare i gestori di eventi come handle
seguito dal nome dell’evento. Vedrai spesso onClick={handleClick}
, onMouseEnter={handleMouseEnter}
, e così via.
In alternativa, puoi definire un gestore di eventi inline nel JSX:
<button onClick={function handleClick() {
alert('You clicked me!');
}}>
Oppure, più sinteticamente, usando una arrow function:
<button onClick={() => {
alert('You clicked me!');
}}>
Tutti questi stili sono equivalenti. I gestori di eventi inline sono comodi per le funzioni brevi.
Leggere le props nei gestori di eventi
Poichè i gestori di eventi sono dichiarati all’interno di un componente, questi hanno accesso alle props del componente. Ecco un bottone che, quando cliccato, mostra un alert con la prop 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> ); }
Questo permette a questi due bottoni di mostrare messaggi differenti. Prova a cambiare i messaggi che gli vengono passati.
Passare i gestori di eventi come props
Spesso vorrai che il componente genitore specifichi il gestore di eventi di un componente figlio. Considera i bottoni: a seconda di dove stai usando un componente Button
, potresti voler eseguire una funzione diversa — forse una riproduce un film ed un’altra carica un’immagine.
Per fare questo, passa una prop che il componente riceve dal suo componente genitore come gestore di eventi in questo modo:
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> ); }
Qui, il componente Toolbar
renderizza un PlayButton
ed un UploadButton
:
PlayButton
passahandlePlayClick
come proponClick
alButton
interno.UploadButton
passa() => alert('Uploading!')
come proponClick
alButton
interno.
Infine, il componente Button
accetta una prop chiamata onClick
. Passa questa prop direttamente al <button>
del browser con onClick={onClick}
. Questo dice a React di chiamare al click la funzione passata.
Se utilizzi un design system, è comune che i componenti come i bottoni contengano lo stile ma non specificano il comportamento. Invece, i componenti come PlayButton
e UploadButton
passeranno i gestori di eventi in basso.
Denominare le props dei gestori di eventi
I componenti integrati come <button>
e <div>
supportano solo i nomi degli eventi del browser come onClick
. Tuttavia, quando stai costruendo i tuoi componenti, puoi chiamare le props dei loro gestori di eventi in qualsiasi modo ti piaccia.
Per convenzione, le props dei gestori di eventi dovrebbero iniziare con on
, seguito da una lettera maiuscola.
Ad esempio, la prop onClick
del componente Button
avrebbe potuto essere chiamata 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> ); }
In questo esempio, <button onClick={onSmash}>
mostra che il <button>
del browser (in minuscolo) necessita di una prop chiamata onClick
, ma il nome della prop ricevuta dal tuo componente Button
custom puoi deciderlo tu!
Quando il tuo componente supporta molteplici interazioni, potresti chiamare le props dei gestori di eventi con concetti specifici all’applicazione. Ad esempio, questo componente Toolbar
riceve i gestori di eventi onPlayMovie
e 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> ); }
Nota come il componente App
non ha bisogno di sapere cosa farà Toolbar
con onPlayMovie
o onUploadImage
. Questo è un dettaglio di implementazione di Toolbar
. Qui, Toolbar
li passa come gestori di eventi onClick
ai suoi Button
, ma potrebbe anche attivarli con una scorciatoia da tastiera. Dare alle props nomi specifici relativi all’applicazione come onPlayMovie
ti dà la flessibilità di cambiare come vengono utilizzate in seguito.
Propagazione degli eventi
I gestori di eventi cattureranno gli eventi da ogni elemento figlio che il tuo componente può possedere. Diciamo che un evento “bolle” o “si propaga” verso l’alto nell’albero: inizia con dove è avvenuto l’evento, e poi sale nell’albero.
Questo <div>
contiene due bottoni. Sia il <div>
ed ogni bottone hanno i propri gestori onClick
. Quali gestori pensi che scatteranno quando cliccherai un bottone?
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> ); }
Se clicchi su entrambi i bottoni, i loro onClick
scatteranno per primi, seguiti dall’onClick
del <div>
genitore. Appariranno quindi due messaggi. Se clicchi sulla toolbar, scatterà soltanto l’onClick
del <div>
genitore.
Stoppare la propagazione
Tutti i gestori di eventi ricevono un oggetto evento come loro unico argomento. Per convenzione, viene solitamente chiamato e
, che sta per “evento”. Puoi usare questo oggetto per leggere informazioni sull’evento.
Questo oggetto evento ti permette anche di stoppare la propagazione. Se vuoi prevenire che un evento raggiunga i componenti genitori, devi chiamare e.stopPropagation()
come fa questo componente 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> ); }
Quando clicchi su un bottone:
- React chiama il gestore
onClick
passato al<button>
. - Quel gestore, definito in
Button
, fa quanto segue:- Chiama
e.stopPropagation()
, prevenendo l’ulteriore propagazione dell’evento. - Chiama la funzione
onClick
, che è una prop passata dal componenteToolbar
.
- Chiama
- Quella funzione, definita nel componente
Toolbar
, mostra l’alert del bottone stesso. - Visto che la propagazione è stata fermata, il gestore
onClick
del genitore<div>
non viene eseguito.
Come risultato di e.stopPropagation()
, cliccando sui bottoni adesso mostra soltanto un singolo alert (dal <button>
) piuttosto che due (dal <button>
e dal <div>
genitore della toolbar). Cliccare un bottone non è la stessa cosa che cliccare la toolbar circostante, quindi fermare la propagazione ha senso per questa UI.
Deep Dive
In rari casi, potresti aver bisogno di catturare tutti gli eventi sugli elementi figli, anche se la propagazione è stata fermata. Per esempio, potresti voler loggare ogni click per gli analytics, indipendentemente dalla logica di propagazione. Puoi farlo aggiungendo Capture
al termine del nome dell’evento:
<div onClickCapture={() => { /* questo scatta per primo */ }}>
<button onClick={e => e.stopPropagation()} />
<button onClick={e => e.stopPropagation()} />
</div>
Ogni evento si propaga in tre fasi:
- Viaggia verso il basso, chiamando tutti i gestori
onClickCapture
. - Esegue il gestore
onClick
dell’elemento cliccato. - Viaggia verso l’alto, chiamando tutti i gestori
onClick
.
Catturare gli eventi è utile per codice come router o analytics, ma probabilmente non li userai nel codice dell’app.
Passare gestori come alternativa alla propagazione
Nota come questo gestore di click esegue una riga di codice e poi chiama la prop onClick
passata dal genitore:
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
Potresti aggiungere più codice a questo gestore prima di chiamare il gestore di eventi onClick
del genitore. Questo pattern fornisce un’alternativa alla propagazione. Permette al componente figlio di gestire l’evento, mentre permette anche al componente genitore di specificare qualche comportamento aggiuntivo. A differenza della propagazione, non è automatico. Ma il vantaggio di questo pattern è che puoi seguire chiaramente tutta la catena di codice che si esegue come risultato di qualche evento.
Se ti affidi alla propagazione e ti risulta difficile tracciare quali gestori vengono eseguiti e perché, prova questo approccio.
Prevenire comportamenti di default
Alcuni eventi del browser hanno un comportamento di default associato ad essi. Per esempio, un evento di submit di un <form>
, che si verifica quando viene cliccato un bottone al suo interno, ricaricherà la pagina di default:
export default function Signup() { return ( <form onSubmit={() => alert('Submitting!')}> <input /> <button>Send</button> </form> ); }
Puoi chiamare e.preventDefault()
sull’oggetto dell’evento per fermare questo comportamento:
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Submitting!'); }}> <input /> <button>Send</button> </form> ); }
Non confondere e.stopPropagation()
e e.preventDefault()
. Sono entrambi utili, ma non sono correlati:
e.stopPropagation()
interrompe l’attivazione dei gestori di eventi associati ai tag soprastanti.e.preventDefault()
impedisce il comportamento di default del browser per i pochi eventi che lo hanno.
I gestori di eventi possono avere effetti collaterali?
Assolutamente! I gestori di eventi sono il posto migliore per gli effetti collaterali.
A differenza delle funzioni di rendering, i gestori di eventi non devono essere puri, quindi è un ottimo posto per cambiare qualcosa, per esempio cambiare il valore di un input in risposta alla digitazione o cambiare una lista in risposta alla pressione di un bottone. Tuttavia, per cambiare qualche informazione, devi prima avere un modo per memorizzarla. In React, questo viene fatto utilizzando lo state, la memoria di un componente. Imparerai tutto su di esso nella prossima pagina.
Riepilogo
- Puoi gestire gli eventi passando una funzione come prop ad un elemento come
<button>
. - I gestori di eventi devono essere passati, non chiamati!.
onClick={handleClick}
, nononClick={handleClick()}
. - Puoi definire una funzione di gestione degli eventi separatamente oppure inline.
- I gestori di eventi sono definiti all’interno di un componente, quindi possono accedere alle props.
- Puoi dichiarare un gestore di eventi in un genitore e passarlo come prop ad un figlio.
- Puoi definire le tue props di gestione degli eventi con nomi specifici dell’applicazione.
- Gli eventi si propagano verso l’alto. Chiama
e.stopPropagation()
sul primo argomento per impedirlo. - Gli eventi possono avere alcuni comportamenti indesiderati di default del browser. Chiama
e.preventDefault()
per impedirlo. - Chiamare esplicitamente una prop di gestione degli eventi da un gestore figlio è un’ottima alternativa alla propagazione.
Sfida 1 di 2: Aggiustare un gestore di eventi
Cliccando questo bottone si suppone che lo sfondo della pagina passi da bianco a nero. Tuttavia, non succede nulla quando lo clicchi. Risolvi il problema. (Non preoccuparti della logica all’interno di handleClick
- quella parte va bene)
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> ); }