Les évènements

Si vous faites du JavaScript et donc du React, vous allez très probablement interagir avec vos éléments. Que ce soit au clique, durant un focus, ou autre, cela vous permet d'avoir des interactions avec votre application. On retrouve fréquemment ces comportements lorsque l'on intéragit avec des formulaires. Seulement, sans React, il est vite difficile de s'organiser avec les éléments du DOM.

La gestion des évènements sur les composants React va vous sembler similaire à celle que l'on utilise au niveau du DOM en mode "inline". Cependant, elle est plus normalisée et le gestionnaire d'évènement est plus puissant que les évènements classiques que l'on connait. A chaque évènement, vous allez fournir une fonction qui devra être exécuter (un callback) alors que sur du JavaScript "inline", vous écrivez littéralement les différentes intructions.

En HTML :

Avec React :

Le SyntheticEvent

ReactJS vous permet d'interagir avec plusieurs types d'évènements (onClick, onChange, ...). En fonction de ces types d'évènements, vous avez plusieurs fonctions disponibles pour intervenir dessus et obtenir les informations dont vous avez besoin. Tout ceci est packagé dans un objet SyntheticEvent

Sur le 2eme console.log, on constate que le prototype est bien un SyntheticEvent

Le SyntheticEvent n'est pas l'évènement "natif" en JavaScript. Ce package proposé par React nous permet d'obtenir un évènement "standardisé" sans que l'on ait à se préoccuper des différences entre les navigateurs. C'est globalement le point qui nous intéresse le plus car nous avons toujours accès à l'ensemble des fonctionnalités natives.

Si pour une raison quelconque vous auriez besoin de l'évènement natif, il est accessible sur la propriété nativeEvent

Voici la liste des attributs disponibles sur l'objet SyntheticEvent :

On retrouve le fameux preventDefault() qui nous sera utile pour stopper la propagation de l'évènement afin d'en prendre son contrôle.

Le recyclage

Chaque object SyntheticEvent est recyclé. C'est à dire que pour tout objet SyntheticEvent, ses propriétés seront remises à null dès que la fonction de rappel de l’événement aura été invoquée. Ce procédé permet à React d'améliorer les performances. Cependant, vous ne pouvez pas accéder à l’événement de façon asynchrone en tentant de le stocker.

Les différents types d'évènements

Vous allez retrouver la plupart des évènements JavaScript que vous connaissez. Pour rappel, la notation sera du camelCase.

Comme vous avez pu le constater, vous n'avez plus à vous soucier de faire des addEventListener ou des removeEventListener. L'évènement s'attache à l'attribut HTML que vous souhaitez et est détruit dès lors que le composant n'est plus visible (unmount). Voici une liste de quelques évènements récurrents :

Les évènements du clavier

  • onKeyDown
  • onKeyPress
  • onKeyUp

On va y retrouver des propriétés comme keyCode qui nous donne le code ASCII de la touche.

Événements de formulaire

  • onChange
  • onInput
  • onInvalid
  • onReset
  • onSubmit

Le onChange est à utiliser notamment sur les inputs checkbox ou radio. Vous obtenez aisément la valeur qui vient d'être modifiée.

Événements de la souris

  • onClick
  • onDoubleClick
  • onDrag
  • onMouseDown
  • onMouseEnter
  • onMouseLeave
  • onMouseMove
  • onMouseOut
  • ...

La liste est plutôt longue mais comme d'habitude, ce sont les évènements classiques que nous avons déjà en JavaScript. Le onClick état l'un des plus courants.

La gestion d'un évènement

Maintenant que nous avons fait le tour des évènements et du coeur du SyntheticEvent, je vais vous présenter comme l'on gère un évènement. Il y a un cas particulier entre une class component et un funcitonal component.

Utilisation d'un class component

En utilisant les classes ES6, il est commun comme notre évènement soit une méthode de la classe pour organiser notre code. Cependant, en JavaScript en général, il vous faut faire attention au this dans les callbacks (fonction de rappel).

Un cours sur le this, le scope et les fonctions de rappel est en préparation. C'est une notion importante en JavaScript

Tentons d'exécuter ce code :

Nous avons une belle erreur!

Et oui! Le this n'est pas accessible car les méthodes de classes ne sont pas liées par défaut. De fait, le this est undefined pour la méthode. Vous êtes dans le scope du callback et non pas celui de la classe. Ce n'est donc pas spécifique à React. Comme je vous le disais, c'est un fonctionnement normal en JavaScript. Ne tombez pas tout de suite sur React en disant "c'est nul".

Pour résoudre cette problématique, 3 solutions s'offrent à vous. Pour garder l'utilisation d'une méthode de ce type, le plus simple est de bind votre fonction en lui rajoutant le contexte de la classe :

Cette méthode peut devenir longue car si vous avez 10 évènements à lier, c'est plutôt répétitif.

Pour aller plus vite, vous pouvez alors utiliser la syntaxe des champs de classe. C'est actuellement un plugin Babel puisque c'est encore au stade expérimental. Si vous avez l'habitude de travailler et d'utiliser Babel, je vous invite à le mettre en place lorsque vous utilisez les classes. C'est très pratique!

Cette syntaxe est activée par défaut dans Create React App

Enfin, la dernière solution est de ne pas déclarer votre méthode de class mais d'utiliser une arrow function directement sur votre élément ou dans la méthode render.

Pour ce genre d'évènement très simple, il est commun d'utiliser ce procédé puisqu'il ne perturbe pas la lisibilité de votre composant. Au contraire, il est facile de lire et de se rendre compte de l'évènement lié au bouton et de son exécution.

Mais si elle n'est pas parfaite ! Cette syntaxe crée une nouvelle fonction de rappel à chaque render. Dans la plupart des cas ce n’est pas gênant sauf si vous l'utilisez pour l'a fournir en tant que propriété d'un composant.

En faisant cela, vous allez impacter les performances de votre application en générant des render surperflus. Vous devriez alors gérer le cas du re-render de votre composant alors qu'il vous suffit d'éviter de déclarer la fonction de cette manière là. Pour ce cas précis, il est donc plus intéressant de déclarer votre fonction en tant que méthode de classe.

Utilisation d'un functional component

Un functional component ne vous permet pas d'accéder aux métohdes de class pour créer vos "handles". Vous allez donc devoir déclarer vos fonctions directement au niveau du render. Pour cela voici la méthode que je vous préconise :

Dans ce cas, vous pouvez aussi écrire directement le callback au niveau du onClick comme nous avons pu le voir juste au dessus. Nous ne sommes pas dans le cas d'un passage de fonction en tant que propriété.