Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
La délégation d'événements de React, l'enveloppement des événements synthétiques et la propagation.
React n'attache pas les écouteurs d'événements directement aux éléments DOM. Il utilise à la place la délégation d'événements — un seul écouteur est attaché au conteneur racine (le div où ReactDOM.createRoot() a été appelé). Lorsqu'un événement natif remonte jusqu'à la racine, React l'intercepte, l'enveloppe dans un objet SyntheticEvent et le distribue dans l'arbre de composants React.
Lorsque vous écrivez onClick='{'handleClick'}' dans du JSX, React n'appelle pas addEventListener sur ce nœud DOM. Au lieu de cela, il enregistre un seul écouteur sur le conteneur racine pour les événements 'click' — en le faisant une fois pour toute l'application, pas une fois par composant.
Lorsque vous cliquez sur un bouton, le navigateur déclenche un événement click natif sur le bouton, qui remonte à travers le vrai arbre DOM jusqu'à la racine du document — où l'écouteur de React attend.
React enveloppe l'événement natif dans un SyntheticEvent — un objet événement normalisé inter-navigateurs qui expose la même API quel que soit le navigateur. Dans les anciennes versions de React, les SyntheticEvents étaient mis en pool et réutilisés pour les performances.
React parcourt l'arbre Fiber depuis l'élément cible jusqu'à la racine, appelant tout gestionnaire onClick (ou autre événement) qu'il trouve sur chaque fiber. Cela simule la propagation native du navigateur mais dans l'arbre virtuel de React.
Tout comme les événements natifs, React prend en charge les gestionnaires de phase de capture via onClickCapture. Ceux-ci se déclenchent de haut en bas (racine → cible) avant que les gestionnaires de phase de propagation ne se déclenchent de bas en haut (cible → racine).
Un seul écouteur à la racine gère tous les événements de ce type. Beaucoup plus efficace que d'attacher des écouteurs à chaque élément interactif.
Le wrapper d'événement inter-navigateurs de React. Normalise les incohérences entre navigateurs. Expose la propriété nativeEvent pour un accès brut.
L'événement se propage depuis l'élément cible vers le haut à travers les éléments ancêtres. Phase par défaut pour la plupart des gestionnaires d'événements React.
L'événement se propage de la racine vers le bas jusqu'à la cible avant la propagation ascendante. Utilisez onClickCapture pour la phase de capture.
Arrête la propagation de l'événement dans l'arbre simulé de React. Ne stoppe PAS l'événement natif à moins d'être appelé sur e.nativeEvent.
1// React attaches ONE listener to the root — not to each button:2// root.addEventListener('click', reactHandler)34function App() {5 return (6 <div onClick={() => console.log('div — bubble')}>7 <button8 onClick={(e) => {9 console.log('button — bubble');10 // e.stopPropagation() would stop div from firing11 }}12 onClickCapture={() => console.log('button — capture')}13 >14 Click me15 </button>16 </div>17 );18}1920// Click order:21// 1. button — capture (capture phase, top-down)22// 2. button — bubble (bubble phase, bottom-up)23// 3. div — bubble (bubble phase, continues up)
La délégation d'événements rend React très efficace — peu importe le nombre de boutons dans votre application, il n'y a qu'un seul écouteur d'événement natif par type d'événement. Cela signifie aussi que React peut intercepter et gérer des événements même pour des composants ajoutés dynamiquement, et que le système d'événements synthétiques de React fonctionne de manière cohérente sur tous les navigateurs.
You add a native `document.addEventListener('click', closeMenu)` to close a dropdown on outside click. Inside the dropdown, you call `e.stopPropagation()` on the React onClick — but the menu still closes.
React's stopPropagation stops propagation within React's synthetic event system (which uses delegation at the root). But the native listener on `document` has already received the event since React 17+ attaches to `root`, not `document`. The native handler fires before React can stop it.
Use `e.nativeEvent.stopImmediatePropagation()` to stop the native event from reaching other native listeners, or better yet, replace the native listener with a React `onClickCapture` on a wrapper div. Keep event handling within React's system to avoid mixed delegation conflicts.
Takeaway: React's event system runs inside a single delegated listener at the root container. Mixing React events with native `addEventListener` creates ordering conflicts. Prefer keeping all event logic within React's system for predictable behavior.
Legacy code from React 16 calls `e.persist()` in event handlers to use the event in async callbacks. After upgrading to React 18, you see that removing `e.persist()` works fine — but no one on the team understands why.
React 16 used event pooling — SyntheticEvent objects were reused after the handler returned, setting all properties to null. Accessing `e.target` in a setTimeout would return null unless you called `e.persist()`. Teams kept adding persist() defensively everywhere.
React 17+ removed event pooling entirely. SyntheticEvent objects are no longer reused — they persist naturally. All `e.persist()` calls can be safely removed. Understanding this change helps teams clean up legacy code and stop defensive coding patterns that are no longer needed.
Takeaway: React's event system evolves across versions. Event pooling removal (React 17), root-level delegation (React 17), and automatic batching in event handlers (React 18) are all changes that affect how you write event handling code. Stay current with the release notes.