Retirer le référentiel de la valeur la plus récente : Gérer les événements avec useEffectEvent
Découvrez comment useEffectEvent dans React 19.2 sépare la logique non réactive d’un effet, élimine les astuces utilisant le ref latest-value, et indique quand cet hook n’est pas l’outil adapté.
De nombreux projets React contiennent un useRef dont la seule fonction est de conserver « la valeur la plus récente » afin qu’un effet n’ait pas besoin d’en dépendre. Cette astuce fonctionne, mais elle cache l’intention réelle. React 19.2 a rendu useEffectEvent stable, offrant ainsi à ce schéma un hook de première classe. À la fin, vous serez en mesure de séparer la logique réactive et non réactive à l’intérieur d’un effet, et de savoir quand ce hook ne s’applique pas.
L’array de dépendances qui ne peut pas gagner
Le conflit apparaît avec des connexions à long terme : salles de chat, flux WebSocket, sessions d’analyse, abonnements. Un ChatRoom reçoit roomId et theme. L’effet doit se reconnecter lorsque roomId change, mais il ne lit theme que pour styliser un message après la connexion.
Ajoutez theme à l’array des dépendances, et le passage en mode sombre détruit inutilement le socket. Si vous l’omettez, la règle de linting des hooks se plaint ; si vous la silencez, le callback conserve theme capturé lors d’un rendu précédent, ce qui constitue un cas classique de fermeture obsolète.
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => {
showNotification('Connected!', theme); // stale after theme changes
});
connection.connect();
return () => connection.disconnect();
}, [roomId, theme]); // theme shouldn't trigger a reconnect
}
La solution trouvée par la plupart des équipes
La correction habituelle consistait à copier theme dans une ref à chaque rendu et à lire ref.current à l’intérieur du callback. Cela fonctionne, mais il est facile d’oublier l’étape de synchronisation, et rien dans le code n’indique que cette logique est délibérément non réactive.
Séparer la logique des événements avec useEffectEvent
useEffectEvent extrait la partie non réactive d’un effet dans sa propre fonction. Cette fonction voit toujours les dernières valeurs des props et de l’état, son appel ne réexécute jamais l’effet, et elle n’est pas incluse dans l’array des dépendances.
Au niveau suivant, la notification passe en mode onConnected. Cet effet se contente de créer la connexion, d’enregistrer le gestionnaire, de se connecter, puis de se déconnecter lors du nettoyage.
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme); // always fresh, never a dependency
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => onConnected());
connection.connect();
return () => connection.disconnect();
}, [roomId]); // only roomId matters now
}
Seul roomId reste comme dépendance. theme n’a pas été caché au linter ; il a simplement été reclasseé. Afficher un message de confirmation lorsqu’une connexion s’établit est une réaction à un événement, il s’agit donc de logique d’événement et non de synchronisation. Changer de thème ne rétablit plus la connexion, et le message suivant utilise toujours le thème actuel.
Conceptuellement, c’est exactement ce que faisait manuellement la manipulation avec le ref : React garde la fonction appelée depuis l’effet en pointant vers la clôture de la dernière rendu, et l’intention est désormais visible.
Avantages
- Aucune clôture obsolète. L’Effet Événement lit les valeurs les plus récentes sans nécessiter de mises à jour manuelles du ref.
Les documents React encouragent les développeurs à séparer les événements des effets depuis l’époque de React 18 ; la version 19.2 soutient enfin ce conseil grâce à un hook stable. Pour en savoir plus sur cette version, consultez notre aperçu de Activity, useEffectEvent et rendu statique dans React 19.2.
useEffectEvent ne remplace pas useEffect, et ce n’est pas non plus un moyen général pour réduire les tableaux de dépendances.
- L’utilisez uniquement lorsque un changement de valeur ne doit pas redémarrer l’effet. Sinon, cette valeur doit figurer dans le tableau.
- N’appeliez jamais un Effect Event pendant le rendu ; invoquez-le depuis l’intérieur des effets, tels que les callbacks de souscription ou les temporiseurs.
- Gardez-le local à son composant plutôt que de le transmettre aux enfants en tant que prop.
- Si la majeure partie d’un effet se trouve à l’intérieur d’un Effect Event, la logique devrait probablement être placée dans un gestionnaire d’événements normal.
Au préalable de la version 19.2, ce hook n’existait que dans des versions expérimentales, donc les projets utilisant 19.0 ou 19.1 doivent être mis à jour pour utiliser l’API stable.
Points clés
- Toute solution de type « conserver la dernière valeur dans un ref » est un candidat pour
useEffectEvent. - Pour chaque dépendance, demandez-vous si un changement doit redémarrer l’effet ; dans le cas contraire, le code qui la lit correspond à de la logique d’événement.
Lectures complémentaires
- Choisir l’outil React adapté : Derive, Handle, Fetch, Defer ou Effect — Un guide de décision pour remplacer les appels reflexifs à useEffect par des valeurs dérivées, des gestionnaires d’événements, une couche de données, useTransition, useMemo mesuré et les API de React 19.
- Arrêter la synchronisation de l’état avec useEffect : Un pattern plus sûr dans React — Découvrez pourquoi l’utilisation d’useEffect pour synchroniser un état dérivé provoque des conditions de course et des rendus supplémentaires, ainsi que la manière de le remplacer par une dérivation en temps de rendu et l’attribut key.
- Construire un state-jouet pour comprendre les snapshots, les files dattente et les effets — Apprenez comment se comportent les hooks React en construisant un petit useState, puis utilisez ce modèle pour expliquer les files dattente de mise à jour, les snapshots obsolètes, les closures, le nettoyage des effets, les refs et le regroupement des opérations.
- Formes asynchrones dans React 19 avec use, useActionState et useOptimistic — Comment use(), useActionState, useFormStatus et useOptimistic remplacent les indicateurs manuels de chargement et derreur dans React 19, ainsi que les pièges que chacun de ces hooks cache discrètement.
- Retirer les dépendances JavaScript legacy : CRA, Moment, jQuery et plus — Quelles bibliothèques et outils JavaScript obsolètes supprimer progressivement, pourquoi chacun représente un risque, qu’est-ce qui les remplace, et comment éviter que votre liste de dépendances ne se détériore à nouveau.