v19.2Latest

Supprimer les dépendances d’un Effet

Lorsque vous écrivez un Effet, le vérificateur de syntaxe s’assure que vous avez inclus chaque valeur réactive (comme les props et l’état) que l’Effet lit dans la liste de ses dépendances. Cela garantit que votre Effet reste synchronisé avec les dernières props et l’état de votre composant. Des dépendances inutiles peuvent faire en sorte que votre Effet s’exécute trop souvent, voire créer une boucle infinie. Suivez ce guide pour examiner et supprimer les dépendances inutiles de vos Effets.

Vous allez apprendre
  • Comment corriger les boucles infinies de dépendances d’Effet
  • Que faire lorsque vous souhaitez supprimer une dépendance
  • Comment lire une valeur depuis votre Effet sans « réagir » à celle-ci
  • Comment et pourquoi éviter les dépendances sur des objets et des fonctions
  • Pourquoi supprimer le vérificateur de dépendances est dangereux, et que faire à la place

Les dépendances doivent correspondre au code

Lorsque vous écrivez un Effet, vous commencez par spécifier commentdémarrer et arrêterce que vous voulez que votre Effet fasse :

Ensuite, si vous laissez les dépendances de l’Effet vides ([]), le vérificateur de syntaxe vous suggérera les dépendances correctes :

Remplissez-les selon ce que dit le vérificateur de syntaxe :

Les Effets « réagissent » aux valeurs réactives.PuisqueroomIdest une valeur réactive (elle peut changer suite à un nouveau rendu), le vérificateur de syntaxe s’assure que vous l’avez spécifiée comme dépendance. SiroomIdreçoit une valeur différente, React resynchronisera votre Effet. Cela garantit que le chat reste connecté à la salle sélectionnée et « réagit » à la liste déroulante :

Pour supprimer une dépendance, prouvez qu'elle n'en est pas une

Notez que vous ne pouvez pas « choisir » les dépendances de votre Effet. Chaquevaleur réactiveutilisée par le code de votre Effet doit être déclarée dans votre liste de dépendances. La liste de dépendances est déterminée par le code environnant :

Les valeurs réactivesincluent les props et toutes les variables et fonctions déclarées directement dans votre composant. PuisqueroomIdest une valeur réactive, vous ne pouvez pas la retirer de la liste de dépendances. Le linter ne le permettrait pas :

Et le linter aurait raison ! PuisqueroomIdpeut changer au fil du temps, cela introduirait un bug dans votre code.

Pour supprimer une dépendance, « prouvez » au linter qu'ellen'a pas besoind'être une dépendance.Par exemple, vous pouvez déplacerroomIdhors de votre composant pour prouver qu'elle n'est pas réactive et ne changera pas lors des nouveaux rendus :

Maintenant queroomIdn'est plus une valeur réactive (et ne peut pas changer lors d'un nouveau rendu), elle n'a pas besoin d'être une dépendance :

C’est pourquoi vous pouvez maintenant spécifier uneliste de dépendances vide ([]).Votre Effetne dépend vraiment plusd’aucune valeur réactive, donc iln’a vraiment pas besoinde se ré-exécuter lorsque les props ou l’état du composant changent.

Pour changer les dépendances, changez le code

Vous avez peut-être remarqué un schéma dans votre flux de travail :

  1. D’abord, vouschangez le codede votre Effet ou la façon dont vos valeurs réactives sont déclarées.
  2. Ensuite, vous suivez les recommandations du linter et ajustez les dépendances pourcorrespondre au code que vous avez modifié.
  3. Si vous n’êtes pas satisfait de la liste des dépendances, vousrevenez à la première étape(et changez à nouveau le code).

La dernière partie est importante.Si vous voulez changer les dépendances, changez d’abord le code environnant.Vous pouvez considérer la liste des dépendances commeune liste de toutes les valeurs réactives utilisées par le code de votre Effet.Vous nechoisissez pasce que vous mettez sur cette liste. La listedécritvotre code. Pour changer la liste des dépendances, changez le code.

Cela peut sembler comme résoudre une équation. Vous pouvez commencer avec un objectif (par exemple, supprimer une dépendance), et vous devez « trouver » le code correspondant à cet objectif. Tout le monde n’aime pas résoudre des équations, et on pourrait dire la même chose de l’écriture d’Effets ! Heureusement, il existe une liste de recettes courantes que vous pouvez essayer ci-dessous.

Piège

Si vous avez une base de code existante, vous pourriez avoir des Effets qui suppriment le linter comme ceci :

Lorsque les dépendances ne correspondent pas au code, il y a un risque très élevé d’introduire des bugs.En supprimant le linter, vous « mentez » à React sur les valeurs dont dépend votre Effet.

Utilisez plutôt les techniques ci-dessous.

Supprimer les dépendances inutiles

Chaque fois que vous ajustez les dépendances de l’Effet pour refléter le code, regardez la liste des dépendances. Est-ce logique que l’Effet se ré-exécute lorsque l’une de ces dépendances change ? Parfois, la réponse est « non » :

  • Vous souhaiterez peut-être réexécuterdifférentes partiesde votre Effet sous différentes conditions.
  • Vous souhaiterez peut-être lire uniquement ladernière valeurd'une dépendance plutôt que de « réagir » à ses changements.
  • Une dépendance peut changer trop souventinvolontairementparce qu'il s'agit d'un objet ou d'une fonction.

Pour trouver la bonne solution, vous devrez répondre à quelques questions concernant votre Effet. Passons-les en revue.

Ce code devrait-il être déplacé vers un gestionnaire d'événements ?

La première chose à laquelle vous devriez penser est de savoir si ce code devrait être un Effet du tout.

Imaginez un formulaire. Lors de la soumission, vous définissez la variable d'étatsubmitted sur true. Vous devez envoyer une requête POST et afficher une notification. Vous avez placé cette logique dans un Effet qui « réagit » au fait quesubmittedsoittrue:

Plus tard, vous souhaitez styliser le message de notification en fonction du thème actuel, vous lisez donc le thème actuel. Commethemeest déclaré dans le corps du composant, c'est une valeur réactive, vous l'ajoutez donc comme dépendance :

En faisant cela, vous avez introduit un bug. Imaginez que vous soumettiez d'abord le formulaire, puis que vous basculiez entre les thèmes Sombre et Clair. Lethemechangera, l'Effet se réexécutera et affichera à nouveau la même notification !

Le problème ici est que cela n'aurait pas dû être un Effet en premier lieu.Vous souhaitez envoyer cette requête POST et afficher la notification en réponse àla soumission du formulaire,qui est une interaction particulière. Pour exécuter du code en réponse à une interaction particulière, placez cette logique directement dans le gestionnaire d'événements correspondant :

Maintenant que le code est dans un gestionnaire d'événements, il n'est pas réactif — il ne s'exécutera donc que lorsque l'utilisateur soumettra le formulaire. En savoir plus surle choix entre les gestionnaires d'événements et les Effetsetcomment supprimer les Effets inutiles.

Votre Effet effectue-t-il plusieurs tâches sans rapport ?

La question suivante que vous devriez vous poser est de savoir si votre Effet effectue plusieurs tâches sans rapport.

Imaginez que vous créez un formulaire d'expédition où l'utilisateur doit choisir sa ville et sa zone. Vous récupérez la liste descitiesdepuis le serveur en fonction ducountrysélectionné pour les afficher dans une liste déroulante :

C'est un bon exemple derécupération de données dans un Effet.Vous synchronisez l'étatcitiesavec le réseau en fonction de la propcountry. Vous ne pouvez pas faire cela dans un gestionnaire d'événements car vous devez récupérer dès queShippingFormest affiché et chaque fois que lecountrychange (peu importe quelle interaction le provoque).

Disons maintenant que vous ajoutez une deuxième liste déroulante pour les zones de la ville, qui devrait récupérer lesareas pour la cityactuellement sélectionnée. Vous pourriez commencer par ajouter un deuxième appelfetchpour la liste des zones dans le même Effet :

Cependant, puisque l'Effet utilise maintenant la variable d'étatcity, vous avez dû ajoutercityà la liste des dépendances. Cela a introduit un problème : lorsque l'utilisateur sélectionne une ville différente, l'Effet se réexécutera et appellerafetchCities(country). Par conséquent, vous allez recharger inutilement la liste des villes de nombreuses fois.

Le problème avec ce code est que vous synchronisez deux choses différentes sans rapport :

  1. Vous voulez synchroniser l'étatcitiesavec le réseau en fonction de la propcountry.
  2. Vous voulez synchroniser l'étatareasavec le réseau en fonction de l'étatcity.

Séparez la logique en deux Effets, chacun réagissant à la prop avec laquelle il doit se synchroniser :

Maintenant, le premier Effet ne se réexécute que sicountrychange, tandis que le second Effet se réexécute lorsquecitychange. Vous les avez séparés par objectif : deux choses différentes sont synchronisées par deux Effets distincts. Deux Effets distincts ont deux listes de dépendances distinctes, ils ne se déclencheront donc pas mutuellement par inadvertance.

Le code final est plus long que l'original, mais séparer ces Effets reste correct.Chaque Effet doit représenter un processus de synchronisation indépendant.Dans cet exemple, supprimer un Effet ne casse pas la logique de l'autre Effet. Cela signifie qu'ilssynchronisent des choses différentes,et il est bon de les séparer. Si la duplication vous préoccupe, vous pouvez améliorer ce code enextrayant la logique répétitive dans un Hook personnalisé.

Lisez-vous un état pour calculer l'état suivant ?

Cet Effet met à jour la variable d'étatmessagesavec un nouveau tableau créé à chaque fois qu'un nouveau message arrive :

Il utilise la variablemessagespourcréer un nouveau tableauqui commence par tous les messages existants et ajoute le nouveau message à la fin. Cependant, puisquemessagesest une valeur réactive lue par un Effet, elle doit être une dépendance :

Et faire demessagesune dépendance introduit un problème.

Chaque fois que vous recevez un message,setMessages()provoque le re-rendu du composant avec un nouveau tableaumessagesqui inclut le message reçu. Cependant, puisque cet Effet dépend maintenant demessages, cela vaégalementre-synchroniser l'Effet. Ainsi, chaque nouveau message fera que le chat se reconnectera. L'utilisateur n'aimerait pas ça !

Pour corriger le problème, ne lisez pasmessagesà l'intérieur de l'Effet. Passez plutôt unefonction de mise à jour à setMessages:

Remarquez que votre Effet ne lit plus du tout la variablemessagesmaintenant.Vous avez seulement besoin de passer une fonction de mise à jour commemsgs => [...msgs, receivedMessage]. Reactplace votre fonction de mise à jour dans une file d’attenteet fournira l’argumentmsgslors du prochain rendu. C’est pourquoi l’Effet lui-même n’a plus besoin de dépendre demessages. Grâce à cette correction, la réception d’un message de discussion ne fera plus se reconnecter la discussion.

Voulez-vous lire une valeur sans « réagir » à ses changements ?

Supposons que vous souhaitiez jouer un son lorsque l’utilisateur reçoit un nouveau message, sauf siisMutedesttrue:

Puisque votre Effet utilise maintenantisMuteddans son code, vous devez l’ajouter aux dépendances :

Le problème est qu’à chaque fois queisMutedchange (par exemple, lorsque l’utilisateur appuie sur le bouton « Muet »), l’Effet se resynchronisera et se reconnectera à la discussion. Ce n’est pas l’expérience utilisateur souhaitée ! (Dans cet exemple, même désactiver le linter ne fonctionnerait pas—si vous le faites,isMutedresterait « bloqué » sur son ancienne valeur.)

Pour résoudre ce problème, vous devez extraire la logique qui ne devrait pas être réactive hors de l’Effet. Vous ne voulez pas que cet Effet « réagisse » aux changements deisMuted.Déplacez cette partie non réactive dans un Événement d’Effet :

Les Événements d’Effet vous permettent de diviser un Effet en parties réactives (qui doivent « réagir » aux valeurs réactives commeroomIdet leurs changements) et en parties non réactives (qui ne lisent que leurs dernières valeurs, commeonMessagelitisMuted).Maintenant que vous lisezisMutedà l’intérieur d’un Événement d’Effet, il n’a plus besoin d’être une dépendance de votre Effet.Par conséquent, la discussion ne se reconnectera pas lorsque vous activerez ou désactiverez le réglage « Muet », résolvant ainsi le problème initial !

Envelopper un gestionnaire d’événement provenant des props

Vous pourriez rencontrer un problème similaire lorsque votre composant reçoit un gestionnaire d’événement comme prop :

Supposons que le composant parent passe une fonctiondifférenteonReceiveMessageà chaque rendu :

PuisqueonReceiveMessageest une dépendance, cela entraînerait la resynchronisation de l’Effet après chaque nouveau rendu du parent. Cela le ferait se reconnecter à la discussion. Pour résoudre ceci, enveloppez l’appel dans un Événement d’Effet :

Les Événements d’Effet ne sont pas réactifs, donc vous n’avez pas besoin de les spécifier comme dépendances. Par conséquent, la discussion ne se reconnectera plus même si le composant parent passe une fonction différente à chaque nouveau rendu.

Séparer le code réactif et non réactif

Dans cet exemple, vous souhaitez enregistrer une visite à chaque fois queroomIdchange. Vous voulez inclure lenotificationCountactuel dans chaque enregistrement, mais vousne voulez pasqu’un changement denotificationCountdéclenche un événement d’enregistrement.

La solution est à nouveau d’extraire le code non réactif dans un Événement d’Effet :

Vous voulez que votre logique soit réactive par rapport àroomId, donc vous lisezroomIddans votre Effet. Cependant, vous ne voulez pas qu’un changement denotificationCountenregistre une visite supplémentaire, donc vous liseznotificationCountdans l’Événement d’Effet.Apprenez-en plus sur la lecture des dernières props et états depuis les Effets en utilisant les Événements d’Effet.

Une valeur réactive change-t-elle involontairement ?

Parfois, vousvoulezque votre Effet « réagisse » à une certaine valeur, mais cette valeur change plus souvent que vous ne le souhaiteriez—et pourrait ne refléter aucun changement réel du point de vue de l’utilisateur. Par exemple, disons que vous créez un objetoptionsdans le corps de votre composant, puis que vous lisez cet objet depuis votre Effet :

Cet objet est déclaré dans le corps du composant, donc c’est unevaleur réactive.Lorsque vous lisez une valeur réactive comme celle-ci dans un Effet, vous la déclarez comme une dépendance. Cela garantit que votre Effet « réagit » à ses changements :

Il est important de la déclarer comme dépendance ! Cela garantit, par exemple, que siroomIdchange, votre Effet se reconnectera au chat avec les nouvellesoptions. Cependant, il y a aussi un problème avec le code ci-dessus. Pour le voir, essayez de taper dans le champ de saisie du bac à sable ci-dessous, et observez ce qui se passe dans la console :

Dans le bac à sable ci-dessus, le champ de saisie ne met à jour que la variable d’étatmessage. Du point de vue de l’utilisateur, cela ne devrait pas affecter la connexion au chat. Cependant, chaque fois que vous mettez à jourmessage, votre composant se re-rend. Lorsque votre composant se re-rend, le code à l’intérieur est exécuté à nouveau depuis le début.

Un nouvel objetoptionsest créé à partir de zéro à chaque re-rendu du composantChatRoom. React voit que l’objetoptionsest unobjet différentde l’objetoptionscréé lors du dernier rendu. C’est pourquoi il resynchronise votre Effet (qui dépend deoptions), et le chat se reconnecte pendant que vous tapez.

Ce problème n’affecte que les objets et les fonctions. En JavaScript, chaque nouvel objet et fonction créé est considéré comme distinct de tous les autres. Peu importe que leur contenu interne soit identique !

Les dépendances de type objet et fonction peuvent faire que votre Effet se resynchronise plus souvent que nécessaire.

C'est pourquoi, autant que possible, vous devriez essayer d'éviter les objets et les fonctions comme dépendances de votre Effet. Essayez plutôt de les déplacer en dehors du composant, à l'intérieur de l'Effet, ou d'en extraire des valeurs primitives.

Déplacer les objets et fonctions statiques en dehors de votre composant

Si l'objet ne dépend d'aucune prop ni d'aucun état, vous pouvez le déplacer en dehors de votre composant :

De cette façon, vousprouvezau linter que ce n'est pas réactif. Il ne peut pas changer à cause d'un nouveau rendu, donc il n'a pas besoin d'être une dépendance. Maintenant, un nouveau rendu deChatRoomne provoquera pas la resynchronisation de votre Effet.

Cela fonctionne aussi pour les fonctions :

PuisquecreateOptionsest déclaré en dehors de votre composant, ce n'est pas une valeur réactive. C'est pourquoi il n'a pas besoin d'être spécifié dans les dépendances de votre Effet, et pourquoi il ne provoquera jamais la resynchronisation de votre Effet.

Déplacer les objets et fonctions dynamiques à l'intérieur de votre Effet

Si votre objet dépend d'une valeur réactive qui peut changer à cause d'un nouveau rendu, comme une proproomId, vous ne pouvez pas le sortiren dehorsde votre composant. Cependant, vous pouvez déplacer sa créationà l'intérieurdu code de votre Effet :

Maintenant queoptionsest déclaré à l'intérieur de votre Effet, ce n'est plus une dépendance de votre Effet. À la place, la seule valeur réactive utilisée par votre Effet estroomId. PuisqueroomIdn'est pas un objet ou une fonction, vous pouvez être sûr qu'il ne sera pasinvolontairementdifférent. En JavaScript, les nombres et les chaînes de caractères sont comparés par leur contenu :

Grâce à cette correction, le chat ne se reconnecte plus si vous modifiez le champ de saisie :

Cependant, ilsereconnecte bien lorsque vous changez la sélection du menu déroulantroomId, comme on pourrait s’y attendre.

Cela fonctionne aussi pour les fonctions :

Vous pouvez écrire vos propres fonctions pour regrouper des morceaux de logique au sein de votre Effet. Tant que vous les déclarez aussià l’intérieurde votre Effet, elles ne sont pas des valeurs réactives, et n’ont donc pas besoin d’être des dépendances de votre Effet.

Lire des valeurs primitives depuis des objets

Parfois, vous pouvez recevoir un objet depuis les props :

Le risque ici est que le composant parent crée l’objet pendant le rendu :

Cela entraînerait une reconnexion de votre Effet à chaque fois que le composant parent refait un rendu. Pour corriger ça, lisez les informations depuis l’objeten dehorsde l’Effet, et évitez d’avoir des dépendances de type objet ou fonction :

La logique devient un peu répétitive (vous lisez certaines valeurs d’un objet en dehors d’un Effet, puis vous créez un objet avec les mêmes valeurs à l’intérieur de l’Effet). Mais cela rend très explicite les informations dont votre Effet dépendréellement. Si un objet est recréé involontairement par le composant parent, le chat ne se reconnecterait pas. Cependant, sioptions.roomIdouoptions.serverUrlsont réellement différents, le chat se reconnecterait.

Calculer des valeurs primitives depuis des fonctions

La même approche peut fonctionner pour les fonctions. Par exemple, supposons que le composant parent passe une fonction :

Pour éviter d’en faire une dépendance (et de provoquer une reconnexion à chaque rendu), appelez-la en dehors de l’Effet. Cela vous donne les valeursroomIdetserverUrlqui ne sont pas des objets, et que vous pouvez lire depuis l’intérieur de votre Effet :

Cela ne fonctionne que pour les fonctionspurescar elles peuvent être appelées sans risque pendant le rendu. Si votre fonction est un gestionnaire d’événement, mais que vous ne voulez pas que ses modifications resynchronisent votre Effet,encapsulez-la plutôt dans un Événement d’Effet.

En résumé

  • Les dépendances doivent toujours correspondre au code.
  • Lorsque vous n'êtes pas satisfait de vos dépendances, ce que vous devez modifier est le code.
  • Supprimer l'avertissement du linter conduit à des bugs très déroutants, et vous devriez toujours l'éviter.
  • Pour supprimer une dépendance, vous devez « prouver » au linter qu'elle n'est pas nécessaire.
  • Si du code doit s'exécuter en réponse à une interaction spécifique, déplacez ce code dans un gestionnaire d'événements.
  • Si différentes parties de votre Effet doivent se ré-exécuter pour différentes raisons, divisez-le en plusieurs Effets.
  • Si vous souhaitez mettre à jour un état en fonction de l'état précédent, passez une fonction de mise à jour.
  • Si vous voulez lire la dernière valeur sans y « réagir », extrayez un Événement d'Effet de votre Effet.
  • En JavaScript, les objets et les fonctions sont considérés comme différents s'ils ont été créés à des moments différents.
  • Essayez d'éviter les dépendances d'objets et de fonctions. Déplacez-les en dehors du composant ou à l'intérieur de l'Effet.

Try out some challenges

Challenge 1 of 4:Fix a resetting interval #

This Effect sets up an interval that ticks every second. You’ve noticed something strange happening: it seems like the interval gets destroyed and re-created every time it ticks. Fix the code so that the interval doesn’t get constantly re-created.