Réagir aux événements
React vous permet d’ajouter des gestionnaires d’événements à votre JSX. Les gestionnaires d’événements sont vos propres fonctions qui seront déclenchées en réponse aux interactions de l’utilisateur telles que des clics, survols, activations de champs de saisie de formulaires, etc.
Vous allez apprendre
- Différentes façons d’écrire un gestionnaire d’événement
- Comment passer la logique de gestion d’événement depuis un composant parent
- Comment les événements se propagent et comment les arrêter
Ajouter des gestionnaires d’événements
Pour ajouter un gestionnaire d’événement, vous devrez d’abord définir une fonction et la passer en tant que prop à la balise JSX appropriée. Par exemple, voici un bouton qui ne fait rien pour le moment :
export default function Button() { return ( <button> Je ne fais rien </button> ); }
Vous pouvez lui faire afficher un message au clic de l’utilisateur en suivant ces trois étapes :
- Déclarez une fonction appelée
handleClick
dans votre composantButton
. - Implémentez la logique de cette fonction (utilisez
alert
pour afficher le message). - Ajoutez
onClick={handleClick}
au<button>
du JSX.
export default function Button() { function handleClick() { alert('Vous avez cliqué !'); } return ( <button onClick={handleClick}> Cliquez ici </button> ); }
Vous avez défini la fonction handleClick
puis l’avez passée en tant que prop à <button>
. handleClick
est un gestionnaire d’événement. Les gestionnaires d’événements sont des fonctions qui :
- Sont généralement définies au sein de vos composants.
- Ont des noms qui commencent par
handle
, suivi du nom de l’événement.
Par convention, on nomme souvent les gestionnaires d’événements en utilisant handle
suivi du nom de l’événement. Vous verrez souvent onClick={handleClick}
, onMouseEnter={handleMouseEnter}
, etc.
Vous pouvez aussi définir un gestionnaire d’événement en ligne dans le JSX :
<button onClick={function handleClick() {
alert('Vous avez cliqué !');
}}>
Ou vous pouvez être plus concis en utilisant une fonction fléchée :
<button onClick={() => {
alert('Vous avez cliqué !');
}}>
Tous ces styles sont équivalents. Les gestionnaires d’événements en ligne sont notamment pratiques pour les fonctions courtes.
Lire les props dans les gestionnaires d’événements
Puisque les gestionnaires d’événements sont déclarés à l’intérieur d’un composant, ils ont accès aux props du composant. Voici un bouton qui, lorsqu’on clique dessus, affiche une alert
avec sa prop message
:
function AlertButton({ message, children }) { return ( <button onClick={() => alert(message)}> {children} </button> ); } export default function Toolbar() { return ( <div> <AlertButton message="Lecture en cours !"> Voir le film </AlertButton> <AlertButton message="Téléversement en cours !"> Téléverser une image </AlertButton> </div> ); }
Ça permet à ces deux boutons d’afficher des messages différents. Essayez de modifier les messages qui leur sont passés.
Passer des gestionnaires d’événements en tant que props
Souvent, vous souhaiterez que le composant parent spécifie le gestionnaire d’événement d’un composant enfant. Prenons l’exemple des boutons : en fonction de l’endroit où vous utilisez un composant Button
, vous voudrez peut-être exécuter une fonction différente, par exemple voir un film ou téléverser une image.
Pour ça, vous devez passer une prop reçue du composant parent en tant que gestionnaire d’événement, comme ceci :
function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } function PlayButton({ movieName }) { function handlePlayClick() { alert(`${movieName} en cours de lecture !`); } return ( <Button onClick={handlePlayClick}> Voir « {movieName} » </Button> ); } function UploadButton() { return ( <Button onClick={() => alert('Téléversement en cours !')}> Téléverser une image </Button> ); } export default function Toolbar() { return ( <div> <PlayButton movieName="Kiki la petite sorcière" /> <UploadButton /> </div> ); }
Dans cet exemple, le composant Toolbar
affiche un PlayButton
et un UploadButton
:
- Dans
PlayButton
, on passehandlePlayClick
à la proponClick
duButton
qu’il contient. - Dans
UploadButton
, on passe() => alert('Téléversement en cours !')
à la proponClick
duButton
qu’il contient.
Enfin, votre composant Button
accepte une prop appelée onClick
qu’il passe ensuite au composant natif <button>
avec onClick={onClick}
. Ça indique à React d’appeler la fonction lors du clic.
Si vous utilisez un Design System, il est courant que des composants tels que les boutons contiennent des styles mais ne spécifient pas de comportement. À la place, des composants tels que PlayButton
et UploadButton
transmettront leurs gestionnaires d’événements.
Nommer les props de gestionnaires d’événements
Les composants natifs tels que <button>
et <div>
ne prennent en charge que les événements navigateur comme onClick
. Cependant, lorsque vous créez vos propres composants, vous pouvez nommer les props de gestionnaires d’événements comme vous le souhaitez.
Par convention, les noms des props de gestionnaires d’événements devraient commencer par on
, suivi d’une lettre majuscule.
Par exemple, on aurait pu nommer la prop onClick
du composant Button
onSmash
:
function Button({ onSmash, children }) { return ( <button onClick={onSmash}> {children} </button> ); } export default function App() { return ( <div> <Button onSmash={() => alert('Lecture en cours !')}> Voir le film </Button> <Button onSmash={() => alert('Téléversement en cours !')}> Téléverser une image </Button> </div> ); }
Dans cet exemple, <button onClick={onSmash}>
montre que le <button>
(en minuscules) natif a toujours besoin d’une prop onClick
mais le nom de la prop de votre composant Button
vous appartient !
Lorsque votre composant prend en charge plusieurs interactions, vous pouvez nommer les props de gestionnaires d’événements en fonction des utilisations spécifiques à votre application. Par exemple, ce composant Toolbar
reçoit les gestionnaires d’événements onPlayMovie
et onUploadImage
:
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Lecture en cours !')} onUploadImage={() => alert('Téléversement en cours !')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Voir le film </Button> <Button onClick={onUploadImage}> Téléverser une image </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Remarquez que le composant App
n’a pas besoin de savoir ce que fera Toolbar
avec onPlayMovie
ou onUploadImage
. C’est un détail d’implémentation de Toolbar
. Ici, Toolbar
les transmet en tant que gestionnaires onClick
à ses Button
s, mais il pourrait également les déclencher ultérieurement avec un raccourci clavier. Nommer les props d’après des interactions spécifiques à l’application telles que onPlayMovie
vous donne de la flexibilité pour modifier leur utilisation ultérieurement.
Propagation d’événements
Les gestionnaires d’événements réagiront également aux événements provenant de tous les enfants que votre composant pourrait avoir. On dit que l’événement « bouillonne » ou « se propage » dans l’arbre : il commence à l’endroit où l’événement s’est produit, puis remonte dans l’arborescence.
L’élément <div>
suivant et ses deux boutons ont leur propre gestionnaire d’événement onClick
. D’après vous, quels gestionnaires d’événements se déclencheront lorsque vous cliquerez sur un bouton ?
export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('Vous avez cliqué sur la barre d’outils !'); }}> <button onClick={() => alert('Lecture en cours !')}> Voir le film </button> <button onClick={() => alert('Téléversement en cours !')}> Téléverser une image </button> </div> ); }
Si vous cliquez sur l’un des boutons, son onClick
sera exécuté en premier, suivi de l’onClick
de l’élément parent <div>
. Ainsi, deux messages vont apparaître. Si vous cliquez sur la barre d’outils elle-même, seul l’onClick
de l’élément <div>
sera exécuté.
Arrêter la propagation
Les gestionnaires d’événements reçoivent un objet événement comme seul argument. Par convention, il est généralement appelé e
, ce qui signifie “event” (« événement », NdT). Vous pouvez utiliser cet objet pour obtenir des informations sur l’événement.
Cet objet événement vous permet également d’arrêter la propagation. Si vous souhaitez empêcher un événement de se propager vers les composants parents, vous devez appeler e.stopPropagation()
, comme le fait le composant Button
:
function Button({ onClick, children }) { return ( <button onClick={e => { e.stopPropagation(); onClick(); }}> {children} </button> ); } export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('Vous avez cliqué sur la barre d’outils !'); }}> <Button onClick={() => alert('Lecture en cours !')}> Voir le film </Button> <Button onClick={() => alert('Téléversement en cours !')}> Téléverser une image </Button> </div> ); }
Quand vous cliquez sur un bouton :
- React appelle le gestionnaire
onClick
passé au<button>
natif. - Ce gestionnaire, défini dans
Button
, effectue les actions suivantes :- Appelle
e.stopPropagation()
, ce qui interrompt la propagation de l’événement. - Appelle la fonction
onClick
, qui est une prop transmise depuis le composantToolbar
.
- Appelle
- Cette fonction, définie dans le composant
Toolbar
, affiche l’alert
spécifique au bouton. - Étant donné que la propagation a été arrêtée, le gestionnaire
onClick
de l’élément parent<div>
ne s’exécute pas.
L’appel à e.stopPropagation()
fait que cliquer sur les boutons n’affiche désormais qu’une seule alert
(du <button>
) au lieu de deux (du <button>
et de l’élément parent <div>
de la barre d’outils). Cliquer sur un bouton ne revient pas à cliquer sur la barre d’outils qui l’enrobe. Il est donc logique d’arrêter la propagation dans cette UI.
En détail
Dans de rares cas, vous pourriez avoir besoin de capturer tous les événements sur les éléments enfants, même s’ils ont arrêté la propagation. Par exemple, vous souhaitez peut-être envoyer des logs dans un outil d’analyse à chaque clic, indépendamment de la logique de propagation. Vous pouvez le faire en ajoutant Capture
à la fin du nom de l’événement :
<div onClickCapture={() => { /* ça s’exécute en premier */ }}>
<button onClick={e => e.stopPropagation()} />
<button onClick={e => e.stopPropagation()} />
</div>
Chaque événement se propage en trois phases :
- Il descend depuis la racine, appelant tous les gestionnaires
onClickCapture
. - Il exécute le gestionnaire
onClick
de l’élément cliqué. - Il remonte, appelant tous les gestionnaires
onClick
.
Les événements de capture sont utiles pour du code tel que les systèmes de routage ou les outils d’analyse, mais vous ne les utiliserez probablement pas dans du code applicatif.
Passer des gestionnaires d’événements au lieu de propager
Vous remarquerez ci-dessous que le gestionnaire du clic exécute une ligne de code puis appelle la prop onClick
passée par le parent :
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
Vous pourriez ajouter davantage de code à ce gestionnaire avant d’appeler le gestionnaire d’événement onClick
du parent. Cette approche offre une alternative à la propagation. Elle permet au composant enfant de gérer l’événement tout en permettant au composant parent de spécifier un comportement supplémentaire. Contrairement à la propagation, ce n’est pas automatique. Mais l’avantage de ce concept tient à ce que vous pouvez suivre clairement l’ensemble de la chaîne de code qui s’exécute en réaction à un événement.
Essayez plutôt cette approche si vous vous appuyez sur la propagation d’événements et qu’il est difficile de retracer quels gestionnaires sont exécutés et pourquoi.
Empêcher le comportement par défaut
Certains événements natifs du navigateur ont un comportement par défaut qui leur est associé. Par exemple, lorsqu’un bouton à l’intérieur d’un élément <form>
est cliqué, l’événement de soumission (submit
) du formulaire se déclenche et, par défaut, provoque le rechargement complet de la page :
export default function Signup() { return ( <form onSubmit={() => alert('Envoi en cours !')}> <input /> <button>Envoyer</button> </form> ); }
Vous pouvez appeler e.preventDefault()
sur l’objet événement pour empêcher ça :
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Envoi en cours !'); }}> <input /> <button>Envoyer</button> </form> ); }
Ne confondez pas e.stopPropagation()
et e.preventDefault()
. Ils sont tous les deux utiles, mais ils sont sans rapport :
e.stopPropagation()
est utilisé pour arrêter la propagation de l’événement vers les éléments parents.e.preventDefault()
est utilisé pour empêcher le comportement par défaut associé à un événement natif du navigateur.
Les gestionnaires d’événements peuvent-ils avoir des effets de bord ?
Absolument ! Les gestionnaires d’événements sont l’endroit idéal pour les effets de bord.
Contrairement aux fonctions de rendu, les gestionnaires d’événements n’ont pas besoin d’être purs. Ce sont donc d’excellents endroits pour changer des trucs, par exemple modifier la valeur d’une saisie en réponse à une frappe, ou modifier une liste en réponse à un appui sur un bouton. Ceci dit, pour modifier des informations, vous avez d’abord besoin d’un moyen de les stocker. En React, ça se fait en utilisant l’état, la mémoire d’un composant. Vous apprendrez tout ça dans la page suivante.
En résumé
- Vous pouvez gérer les événements en passant la fonction gestionnaire en tant que prop à un élément comme
<button>
. - Les gestionnaires d’événements doivent être passés, pas appelés ! Utilisez
onClick={handleClick}
, pasonClick={handleClick()}
. - Vous pouvez définir une fonction de gestion d’événement hors du JSX ou en ligne.
- Les gestionnaires d’événements sont définis au sein du composant, ce qui leur permet d’accéder à ses props et son état local.
- Vous pouvez déclarer un gestionnaire d’événement dans un composant parent et le passer en tant que prop à un enfant.
- Vous pouvez définir vos propres noms de gestionnaires d’événements spécifiques à l’application.
- Les événements se propagent vers le haut. Appelez
e.stopPropagation()
sur le premier argument pour empêcher ça. - Les événements peuvent avoir des comportements navigateur par défaut que vous ne souhaitez pas. Appelez
e.preventDefault()
pour les empêcher. - Appeler explicitement une prop de gestionnaire d’événement depuis un gestionnaire d’événement enfant est une bonne alternative à la propagation.
Défi 1 sur 2 · Réparer un gestionnaire d’événement
Cliquer sur ce bouton est censé alterner la couleur de l’arrière-plan de la page entre blanc et noir. Cependant, rien ne se produit lorsque vous cliquez dessus. Corrigez le problème. (Ne vous inquiétez pas de la logique à l’intérieur de handleClick
- cette partie est correcte).
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()}> Basculer la lumière </button> ); }