Débutant20 min de lecture

Gestion des événements

Apprends comment React gère les interactions utilisateur avec son système d'événements synthétiques, incluant les clics, les entrées de formulaire et les événements clavier.

Événements en React vs. JavaScript pur

En JavaScript pur, tu gères les événements en appelant addEventListener sur un élément DOM. En React, tu gères les événements directement dans le JSX en utilisant des props spéciales qui commencent par on suivi du nom de l'événement en camelCase.

JavaScript purReact JSX
element.addEventListener('click', handler)<button onClick={handler}>
element.addEventListener('change', handler)<input onChange={handler}>
element.addEventListener('submit', handler)<form onSubmit={handler}>
element.addEventListener('keydown', handler)<input onKeyDown={handler}>
element.addEventListener('mouseover', handler)<div onMouseOver={handler}>

Remarque la convention de nommage : toutes les props d'événements utilisent le camelCaseonClick et non onclick, onChange et non onchange, onKeyDown et non onkeydown.

Voici un simple gestionnaire de clic :

jsx
function App() {
  function handleClick() {
    alert('Le bouton a été cliqué !');
  }

  return <button onClick={handleClick}>Clique-moi</button>;
}

Important : Tu passes la référence de la fonction, pas un appel de fonction :

jsx
// CORRECT — passe la référence de la fonction
<button onClick={handleClick}>Cliquer</button>

// INCORRECT — appelle la fonction immédiatement au rendu !
<button onClick={handleClick()}>Cliquer</button>

Avec la mauvaise version, handleClick() s'exécute à chaque fois que le composant est rendu, pas quand le bouton est cliqué. C'est une erreur très courante chez les débutants.

Tu peux aussi utiliser des fonctions flèches inline pour des gestionnaires courts :

jsx
<button onClick={() => console.log('Cliqué !')}>Cliquer</button>

L'objet événement synthétique

Quand un événement se déclenche, React passe un objet SyntheticEvent à ta fonction gestionnaire. C'est le propre objet événement de React qui enveloppe l'événement natif du navigateur et fournit une API cohérente sur tous les navigateurs.

jsx
function App() {
  function handleClick(event) {
    console.log(event.type);    // 'click'
    console.log(event.target);  // l'élément DOM cliqué
    console.log(event.clientX); // position X de la souris
  }

  return <button onClick={handleClick}>Clique-moi</button>;
}

Le SyntheticEvent a la même interface que l'événement natif du navigateur, donc toutes les propriétés que tu connais du JavaScript vanilla fonctionnent : event.target, event.currentTarget, event.type, etc.

event.preventDefault() fonctionne comme en JavaScript vanilla — il empêche le comportement par défaut du navigateur :

jsx
function ContactForm() {
  function handleSubmit(event) {
    event.preventDefault(); // Empêche le rechargement de la page
    console.log('Formulaire soumis !');
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="email" />
      <button type="submit">Envoyer</button>
    </form>
  );
}

Sans preventDefault(), soumettre un formulaire provoque le rechargement de la page par le navigateur. Dans une application React, tu veux presque toujours empêcher cela et gérer la soumission en JavaScript à la place.

event.stopPropagation() empêche l'événement de remonter vers les éléments parents :

jsx
function App() {
  return (
    <div onClick={() => console.log('div cliqué')}>
      <button onClick={(e) => {
        e.stopPropagation();
        console.log('bouton cliqué');
      }}>
        Clique-moi
      </button>
    </div>
  );
}
// Affiche seulement 'bouton cliqué', pas 'div cliqué'

Gestion des changements d'entrée

Un des patterns d'événements les plus courants en React est la gestion des entrées de texte. Tu utilises l'événement onChange combiné avec useState pour créer ce que React appelle un composant contrôlé — une entrée dont la valeur est pilotée par l'état React.

jsx
function SearchBox() {
  const [query, setQuery] = React.useState('');

  function handleChange(event) {
    setQuery(event.target.value);
  }

  return (
    <div>
      <input 
        type="text" 
        value={query} 
        onChange={handleChange} 
      />
      <p>Recherche pour : {query}</p>
    </div>
  );
}

Voici ce qui se passe à chaque frappe de touche :

  1. L'utilisateur tape un caractère dans l'entrée.
  2. React déclenche l'événement onChange.
  3. handleChange appelle setQuery(event.target.value) avec la nouvelle valeur de l'entrée.
  4. React re-rend le composant.
  5. L'entrée affiche la nouvelle valeur de query.

L'attribut value={query} fait de ceci une entrée contrôlée — React est la source unique de vérité pour la valeur de l'entrée. Cela te donne un contrôle total : tu peux valider, transformer ou rejeter l'entrée avant qu'elle n'apparaisse.

jsx
// Autoriser seulement les chiffres
function NumberInput() {
  const [value, setValue] = React.useState('');

  function handleChange(event) {
    const newValue = event.target.value;
    // Mettre à jour seulement si l'entrée contient des chiffres ou est vide
    if (/^\d*$/.test(newValue)) {
      setValue(newValue);
    }
  }

  return <input value={value} onChange={handleChange} />;
}

Tu peux aussi écrire le gestionnaire inline pour les cas plus simples :

jsx
<input 
  value={query} 
  onChange={(e) => setQuery(e.target.value)} 
/>

Passer des arguments aux gestionnaires d'événements

Parfois tu dois passer des données supplémentaires à un gestionnaire d'événements — par exemple, quel élément d'une liste a été cliqué. Le pattern standard est d'envelopper le gestionnaire dans une fonction flèche :

jsx
function TodoList() {
  const [todos, setTodos] = React.useState([
    { id: 1, text: 'Apprendre React' },
    { id: 2, text: 'Construire un projet' },
    { id: 3, text: 'Le déployer' },
  ]);

  function handleDelete(id) {
    setTodos(todos.filter(todo => todo.id !== id));
  }

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>
          {todo.text}
          <button onClick={() => handleDelete(todo.id)}>
            Supprimer
          </button>
        </li>
      ))}
    </ul>
  );
}

La fonction flèche () => handleDelete(todo.id) crée une nouvelle fonction qui "capture" le todo.id spécifique pour chaque élément de la liste. Quand le bouton est cliqué, elle appelle handleDelete avec l'ID correct.

Tu peux toujours accéder à l'objet événement en plus de tes arguments personnalisés :

jsx
function handleClick(id, event) {
  console.log('ID de l\'élément :', id);
  console.log('Événement :', event.type);
}

<button onClick={(event) => handleClick(item.id, event)}>
  Cliquer
</button>

Types d'événements courants que tu utiliseras souvent :

  • onClick — Boutons, éléments cliquables
  • onChange — Entrées de texte, sélecteurs, cases à cocher
  • onSubmit — Formulaires
  • onKeyDown / onKeyUp — Raccourcis clavier
  • onFocus / onBlur — Suivi du focus des entrées
  • onMouseEnter / onMouseLeave — Effets de survol

Événements en action : Recherche en direct

html
<div id="root"></div>
<script src="https://unpkg.com/react@19/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@19/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
  function App() {
    const [query, setQuery] = React.useState("");
    const fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape"];
    
    const filtered = fruits.filter(fruit =>
      fruit.toLowerCase().includes(query.toLowerCase())
    );

    return (
      <div style={{ padding: "20px" }}>
        <h2>Recherche de fruits</h2>
        <input
          type="text"
          placeholder="Rechercher des fruits..."
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          style={{ padding: "8px", fontSize: "16px", width: "200px" }}
        />
        <ul>
          {filtered.map((fruit, i) => (
            <li key={i}>{fruit}</li>
          ))}
        </ul>
        <p>{filtered.length} résultat(s)</p>
      </div>
    );
  }

  ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>

Quelle est la différence entre `onClick={handleClick}` et `onClick={handleClick()}` ?

Prêt à pratiquer ?

Crée ton compte gratuit pour accéder à l'éditeur de code interactif, lancer les défis et suivre ta progression.