Séparer les événements des effets
Les gestionnaires d'événements ne se réexécutent que lorsque vous effectuez à nouveau la même interaction. Contrairement aux gestionnaires d'événements, les effets se resynchronisent si une valeur qu'ils lisent, comme une prop ou une variable d'état, est différente de ce qu'elle était lors du dernier rendu. Parfois, vous voulez aussi un mélange des deux comportements : un effet qui se réexécute en réponse à certaines valeurs mais pas à d'autres. Cette page vous apprendra comment faire cela.
Vous apprendrez
- Comment choisir entre un gestionnaire d'événements et un effet
- Pourquoi les effets sont réactifs, et les gestionnaires d'événements ne le sont pas
- Que faire lorsque vous voulez qu'une partie du code de votre effet ne soit pas réactive
- Ce que sont les événements d'effet, et comment les extraire de vos effets
- Comment lire les dernières props et l'état depuis les effets en utilisant les événements d'effet
Choisir entre les gestionnaires d'événements et les effets
Tout d'abord, rappelons la différence entre les gestionnaires d'événements et les effets.
Imaginez que vous implémentez un composant de salon de discussion. Vos exigences ressemblent à ceci :
- Votre composant doit se connecter automatiquement au salon de discussion sélectionné.
- Lorsque vous cliquez sur le bouton « Envoyer », il doit envoyer un message au chat.
Disons que vous avez déjà implémenté le code pour eux, mais vous ne savez pas où le mettre. Devriez-vous utiliser des gestionnaires d'événements ou des effets ? Chaque fois que vous devez répondre à cette question, considérezpourquoi le code doit s'exécuter.
Les gestionnaires d'événements s'exécutent en réponse à des interactions spécifiques
Du point de vue de l'utilisateur, l'envoi d'un message devrait se produireparce quele bouton « Envoyer » spécifique a été cliqué. L'utilisateur sera plutôt contrarié si vous envoyez son message à tout autre moment ou pour toute autre raison. C'est pourquoi l'envoi d'un message devrait être un gestionnaire d'événements. Les gestionnaires d'événements vous permettent de gérer des interactions spécifiques :
Avec un gestionnaire d'événements, vous pouvez être sûr quesendMessage(message)ne s'exécuteraquesi l'utilisateur appuie sur le bouton.
Les effets s'exécutent chaque fois qu'une synchronisation est nécessaire
Rappelez-vous que vous devez aussi garder le composant connecté au salon de discussion. Où ce code doit-il aller ?
Laraisond'exécuter ce code n'est pas une interaction particulière. Peu importe pourquoi ou comment l'utilisateur a navigué vers l'écran de la salle de discussion. Maintenant qu'il le regarde et pourrait interagir avec, le composant doit rester connecté au serveur de discussion sélectionné. Même si le composant de salle de discussion était l'écran initial de votre application, et que l'utilisateur n'a effectué aucune interaction, vous aurieztout de mêmebesoin de vous connecter. C'est pourquoi c'est un Effet :
Avec ce code, vous pouvez être sûr qu'il y a toujours une connexion active au serveur de discussion actuellement sélectionné,indépendammentdes interactions spécifiques effectuées par l'utilisateur. Que l'utilisateur ait seulement ouvert votre application, sélectionné une salle différente, ou navigué vers un autre écran et soit revenu, votre Effet garantit que le composantrestera synchroniséavec la salle actuellement sélectionnée, etse reconnectera chaque fois que cela sera nécessaire.
Valeurs réactives et logique réactive
Intuitivement, on pourrait dire que les gestionnaires d'événements sont toujours déclenchés « manuellement », par exemple en cliquant sur un bouton. Les Effets, en revanche, sont « automatiques » : ils s'exécutent et se réexécutent aussi souvent que nécessaire pour rester synchronisés.
Il existe une manière plus précise d'y penser.
Les props, l'état et les variables déclarées dans le corps de votre composant sont appeléesvaleurs réactives. Dans cet exemple,serverUrln'est pas une valeur réactive, maisroomIdetmessagele sont. Elles participent au flux de données du rendu :
Des valeurs réactives comme celles-ci peuvent changer à cause d'un nouveau rendu. Par exemple, l'utilisateur peut modifier lemessageou choisir un autreroomIddans une liste déroulante. Les gestionnaires d'événements et les Effets réagissent différemment aux changements :
- La logique à l'intérieur des gestionnaires d'événements estnon réactive.Elle ne s'exécutera pas à nouveau à moins que l'utilisateur ne répète la même interaction (par exemple, un clic). Les gestionnaires d'événements peuvent lire des valeurs réactives sans « réagir » à leurs changements.
- La logique à l'intérieur des Effets estréactive.Si votre Effet lit une valeur réactive,vous devez la spécifier comme dépendance.Ensuite, si un nouveau rendu provoque un changement de cette valeur, React réexécutera la logique de votre Effet avec la nouvelle valeur.
Revenons à l'exemple précédent pour illustrer cette différence.
La logique à l'intérieur des gestionnaires d'événements est non réactive
Regardez cette ligne de code. Cette logique devrait-elle être réactive ou non ?
Du point de vue de l'utilisateur,un changement dumessagene signifiepasqu'il souhaite envoyer un message.Cela signifie seulement que l'utilisateur est en train de taper. En d'autres termes, la logique qui envoie un message ne devrait pas être réactive. Elle ne devrait pas s'exécuter à nouveau simplement parce que lavaleur réactivea changé. C'est pourquoi elle appartient au gestionnaire d'événements :
Les gestionnaires d'événements ne sont pas réactifs, doncsendMessage(message)ne s'exécutera que lorsque l'utilisateur cliquera sur le bouton Envoyer.
La logique à l'intérieur des Effets est réactive
Revenons maintenant à ces lignes :
Du point de vue de l'utilisateur,un changement duroomIdsignifiequ'il souhaite se connecter à une salle différente.En d'autres termes, la logique de connexion à la salle devrait être réactive. Vousvoulezque ces lignes de code « suivent » lavaleur réactiveet s'exécutent à nouveau si cette valeur est différente. C'est pourquoi elle appartient à un Effet :
Les Effets sont réactifs, donccreateConnection(serverUrl, roomId)etconnection.connect()s'exécuteront pour chaque valeur distincte deroomId. Votre Effet maintient la connexion de chat synchronisée avec la salle actuellement sélectionnée.
Extraire la logique non réactive des Effets
Les choses se compliquent lorsque vous souhaitez mélanger une logique réactive avec une logique non réactive.
Par exemple, imaginez que vous souhaitiez afficher une notification lorsque l'utilisateur se connecte au chat. Vous lisez le thème actuel (sombre ou clair) dans les props afin d'afficher la notification dans la bonne couleur :
Cependant,themeest une valeur réactive (elle peut changer à la suite d'un nouveau rendu), ettoute valeur réactive lue par un Effet doit être déclarée comme dépendance.Vous devez maintenant spécifierthemecomme dépendance de votre Effet :
Jouez avec cet exemple et voyez si vous pouvez repérer le problème avec cette expérience utilisateur :
Lorsque leroomIdchange, le chat se reconnecte comme prévu. Mais commethemeest aussi une dépendance, le chatse reconnecte égalementchaque fois que vous basculez entre le thème sombre et le thème clair. Ce n'est pas idéal !
En d'autres termes, vousne voulez pasque cette ligne soit réactive, même si elle se trouve à l'intérieur d'un Effet (qui est réactif) :
Vous avez besoin d'un moyen de séparer cette logique non réactive de l'Effet réactif qui l'entoure.
Déclarer un Événement d'Effet
Utilisez un Hook spécial appeléuseEffectEventpour extraire cette logique non réactive de votre Effet :
Ici,onConnectedest appelé unÉvénement d'Effet.C'est une partie de la logique de votre Effet, mais il se comporte beaucoup plus comme un gestionnaire d'événements. La logique à l'intérieur n'est pas réactive, et elle « voit » toujours les dernières valeurs de vos props et de votre état.
Vous pouvez maintenant appeler l'Événement d'EffetonConnecteddepuis l'intérieur de votre Effet :
Cela résout le problème. Notez que vous avez dûretirerthemede la liste des dépendances de votre Effet, car il n'est plus utilisé dans l'Effet. Vous n'avez pas non plus besoin deajouteronConnected, carles Événements d'Effet ne sont pas réactifs et doivent être omis des dépendances.
Vérifiez que le nouveau comportement fonctionne comme prévu :
Vous pouvez considérer les Événements d'Effet comme étant très similaires aux gestionnaires d'événements. La principale différence est que les gestionnaires d'événements s'exécutent en réponse à des interactions utilisateur, tandis que les Événements d'Effet sont déclenchés par vous depuis des Effets. Les Événements d'Effet vous permettent de « rompre la chaîne » entre la réactivité des Effets et le code qui ne devrait pas être réactif.
Lire les dernières props et l'état avec les Événements d'Effet
Les Événements d'Effet vous permettent de corriger de nombreux motifs où vous pourriez être tenté de supprimer l'avertissement du linter de dépendances.
Par exemple, disons que vous avez un Effet pour enregist
Ensuite, vous ajoutez plusieurs routes à votre site. Maintenant, votre composantPagereçoit une propurlavec le chemin actuel. Vous souhaitez passer l'urlcomme partie de votre appellogVisit, mais le linter de dépendances se plaint :
Réfléchissez à ce que vous voulez que le code fasse. Vousvoulezenregistrer une visite distincte pour différentes URL, car chaque URL représente une page différente. En d'autres termes, cet appellogVisit devraitêtre réactif par rapport à l'url. C'est pourquoi, dans ce cas, il est logique de suivre le linter de dépendances et d'ajouterurlcomme dépendance :
Supposons maintenant que vous souhaitiez inclure le nombre d'articles dans le panier d'achat avec chaque visite de page :
Vous avez utilisénumberOfItemsà l'intérieur de l'Effet, donc le linter vous demande de l'ajouter comme dépendance. Cependant, vousne voulez pasque l'appellogVisitsoit réactif par rapport ànumberOfItems. Si l'utilisateur ajoute quelque chose au panier d'achat et que lenumberOfItemschange, celane signifie pasque l'utilisateur a visité la page à nouveau. En d'autres termes,visiter la pageest, en un sens, un « événement ». Cela se produit à un moment précis.
Divisez le code en deux parties :
Ici,onVisitest un Événement d'Effet. Le code à l'intérieur n'est pas réactif. C'est pourquoi vous pouvez utilisernumberOfItems(ou toute autre valeur réactive !) sans craindre que cela ne provoque la ré-exécution du code environnant lors des changements.
D'un autre côté, l'Effet lui-même reste réactif. Le code à l'intérieur de l'Effet utilise la propurl, donc l'Effet se ré-exécutera après chaque nouveau rendu avec uneurldifférente. Cela, à son tour, appellera l'Événement d'EffetonVisit.
En conséquence, vous appellerezlogVisitpour chaque changement de l'url, et vous lirez toujours le derniernumberOfItems. Cependant, sinumberOfItemschange par lui-même, cela ne provoquera pas la ré-exécution du code.
Remarque
Vous vous demandez peut-être si vous pourriez appeleronVisit()sans arguments et lire l'urlà l'intérieur :
Cela fonctionnerait, mais il est préférable de passer cetteurlexplicitement à l'Événement d'Effet.En passant l'urlcomme argument à votre Événement d'Effet, vous indiquez que visiter une page avec uneurldifférente constitue un « événement » distinct du point de vue de l'utilisateur.LevisitedUrlfaitpartiede l'« événement » qui s'est produit :
Puisque votre Événement d'Effet « demande » explicitement levisitedUrl, vous ne pouvez plus supprimer accidentellement l'urldes dépendances de l'Effet. Si vous supprimez la dépendanceurl(ce qui ferait que des visites de pages distinctes soient comptées comme une seule), le linter vous avertira. Vous voulez queonVisitsoit réactif par rapport à l'url, donc au lieu de lire l'urlà l'intérieur (où elle ne serait pas réactive), vous la passezdepuisvotre Effet.
Cela devient particulièrement important s'il y a une logique asynchrone à l'intérieur de l'Effet :
Ici, l'urlà l'intérieur deonVisitcorrespond à ladernière
Limitations des événements d'Effet
Les événements d'Effet sont très limités dans leur utilisation :
- Ne les appelez qu'à l'intérieur d'Effets.
- Ne les passez jamais à d'autres composants ou Hooks.
Par exemple, ne déclarez et ne passez pas un événement d'Effet comme ceci :
Au lieu de cela, déclarez toujours les événements d'Effet directement à côté des Effets qui les utilisent :
Les événements d'Effet sont des « morceaux » non réactifs de votre code d'Effet. Ils doivent être à côté de l'Effet qui les utilise.
Récapitulatif
- Les gestionnaires d'événements s'exécutent en réponse à des interactions spécifiques.
- Les Effets s'exécutent chaque fois qu'une synchronisation est nécessaire.
- La logique à l'intérieur des gestionnaires d'événements n'est pas réactive.
- La logique à l'intérieur des Effets est réactive.
- Vous pouvez déplacer la logique non réactive des Effets vers les événements d'Effet.
- N'appelez les événements d'Effet qu'à l'intérieur d'Effets.
- Ne passez pas les événements d'Effet à d'autres composants ou Hooks.
Try out some challenges
Challenge 1 of 4:Fix a variable that doesn’t update #
This Timer component keeps a count state variable which increases every second. The value by which it’s increasing is stored in the increment state variable. You can control the increment variable with the plus and minus buttons.
However, no matter how many times you click the plus button, the counter is still incremented by one every second. What’s wrong with this code? Why is increment always equal to 1 inside the Effect’s code? Find the mistake and fix it.
