Cycle de vie des Effets réactifs
Les Effets ont un cycle de vie différent de celui des composants. Les composants peuvent être montés, mis à jour ou démontés. Un Effet ne peut faire que deux choses : démarrer la synchronisation de quelque chose, puis plus tard l'arrêter. Ce cycle peut se produire plusieurs fois si votre Effet dépend de props et d'états qui changent au fil du temps. React fournit une règle de linter pour vérifier que vous avez correctement spécifié les dépendances de votre Effet. Cela permet à votre Effet de rester synchronisé avec les dernières props et états.
Vous apprendrez
- En quoi le cycle de vie d'un Effet diffère de celui d'un composant
- Comment considérer chaque Effet individuellement de manière isolée
- Quand votre Effet doit se resynchroniser, et pourquoi
- Comment les dépendances de votre Effet sont déterminées
- Ce que signifie qu'une valeur soit réactive
- Ce que signifie un tableau de dépendances vide
- Comment React vérifie que vos dépendances sont correctes avec un linter
- Que faire lorsque vous n'êtes pas d'accord avec le linter
Le cycle de vie d'un Effet
Chaque composant React passe par le même cycle de vie :
- Un composantest montélorsqu'il est ajouté à l'écran.
- Un composantest mis à jourlorsqu'il reçoit de nouvelles props ou un nouvel état, généralement en réponse à une interaction.
- Un composantest démontélorsqu'il est retiré de l'écran.
C'est une bonne façon de penser aux composants, maispasaux Effets.Essayez plutôt de penser à chaque Effet indépendamment du cycle de vie de votre composant. Un Effet décrit commentsynchroniser un système externeavec les props et l'état actuels. À mesure que votre code change, la synchronisation devra se produire plus ou moins souvent.
Pour illustrer ce point, considérez cet Effet qui connecte votre composant à un serveur de discussion :
Le corps de votre Effet spécifie commentdémarrer la synchronisation :
La fonction de nettoyage retournée par votre Effet spécifie commentarrêter la synchronisation :
Intuitivement, on pourrait penser que Reactdémarrerait la synchronisationlorsque votre composant est monté etarrêterait la synchronisationlorsque votre composant est démonté. Cependant, ce n'est pas toute l'histoire ! Parfois, il peut aussi être nécessaire dedémarrer et arrêter la synchronisation plusieurs foisalors que le composant reste monté.
Voyonspourquoicela est nécessaire,quandcela se produit, etcommentvous pouvez contrôler ce comportement.
Note
Certains Effets ne retournent pas du tout de fonction de nettoyage.Le plus souvent,vous voudrez en retourner une—mais si vous ne le faites pas, React se comportera comme si vous aviez retourné une fonction de nettoyage vide.
Pourquoi la synchronisation peut devoir se produire plus d'une fois
Imaginez que ce composantChatRoomreçoive une proproomIdque l'utilisateur choisit dans un menu déroulant. Supposons qu'initialement l'utilisateur choisisse la salle"general"commeroomId. Votre application affiche la salle de discussion"general" :
Une fois l'interface utilisateur affichée, React exécutera votre Effet pourdémarrer la synchronisation.Il se connecte à la salle"general" :
Jusqu'ici, tout va bien.
Ensuite, l'utilisateur choisit une salle différente dans le menu déroulant (par exemple,"travel"). D'abord, React mettra à jour l'interface utilisateur :
Réfléchissez à ce qui devrait se passer ensuite. L'utilisateur voit que"travel"est la salle de discussion sélectionnée dans l'interface. Cependant, l'Effet qui s'est exécuté la dernière fois est toujours connecté à la salle"general". La proproomIda changé, donc ce que votre Effet a fait à ce moment-là (se connecter à la salle"general") ne correspond plus à l'interface utilisateur.
À ce stade, vous voulez que React fasse deux choses :
- Arrêter la synchronisation avec l'ancienne
roomId(se déconnecter de la salle"general") - Commencer la synchronisation avec la nouvelle
roomId(se connecter à la salle"travel")
Heureusement, vous avez déjà appris à React comment faire ces deux choses !Le corps de votre Effet spécifie comment commencer la synchronisation, et votre fonction de nettoyage spécifie comment l'arrêter. Tout ce dont React a besoin maintenant, c'est de les appeler dans le bon ordre et avec les bonnes props et le bon état. Voyons comment cela se produit exactement.
Comment React resynchronise votre Effet
Rappelez-vous que votre composantChatRooma reçu une nouvelle valeur pour sa proproomId. Elle était"general", et maintenant elle est"travel". React doit resynchroniser votre Effet pour vous reconnecter à une salle différente.
Pourarrêter la synchronisation,React appellera la fonction de nettoyage que votre Effet a retournée après s'être connecté à la salle"general". PuisqueroomIdétait"general", la fonction de nettoyage se déconnecte de la salle"general" :
Ensuite, React exécutera l'Effet que vous avez fourni pendant ce rendu. Cette fois,roomIdest"travel", donc il vacommencer la synchronisationavec la salle de discussion"travel"(jusqu'à ce que sa fonction de nettoyage soit également appelée plus tard) :
Grâce à cela, vous êtes maintenant connecté à la même salle que celle choisie par l'utilisateur dans l'interface. Catastrophe évitée !
Chaque fois que votre composant se re-rend avec uneroomIddifférente, votre Effet se resynchronisera. Par exemple, supposons que l'utilisateur changeroomIdde"travel" à "music". React va à nouveauarrêter la synchronisationde votre Effet en appelant sa fonction de nettoyage (vous déconnectant de la salle"travel"). Ensuite, il varecommencer la synchronisationen exécutant son corps avec la nouvelle proproomId(vous connectant à la salle"music").
Enfin, lorsque l'utilisateur passe à un autre écran,ChatRoomest démonté. Maintenant, il n'y a plus du tout besoin de rester connecté. React vaarrêter la synchronisationde votre Effet une dernière fois et vous déconnecter de la salle de discussion"music".
Penser du point de vue de l'Effet
Récapitulons tout ce qui s'est passé du point de vue du composantChatRoom :
ChatRoommonté avecroomIddéfini sur"general"ChatRoommis à jour avecroomIddéfini sur"travel"ChatRoommis à jour avecroomIddéfini sur"music"ChatRoomdémonté
À chacun de ces moments du cycle de vie du composant, votre Effet a fait des choses différentes :
- Votre Effet s’est connecté à la salle
"general" - Votre Effet s’est déconnecté de la salle
"general"et s’est connecté à la salle"travel" - Votre Effet s’est déconnecté de la salle
"travel"et s’est connecté à la salle"music" - Votre Effet s’est déconnecté de la salle
"music"
Maintenant, réfléchissons à ce qui s’est passé du point de vue de l’Effet lui-même :
La structure de ce code pourrait vous inspirer à voir ce qui s’est passé comme une séquence de périodes de temps non chevauchantes :
- Votre Effet s’est connecté à la salle
"general"(jusqu’à ce qu’il se déconnecte) - Votre Effet s’est connecté à la salle
"travel"(jusqu’à ce qu’il se déconnecte) - Votre Effet s’est connecté à la salle
"music"(jusqu’à ce qu’il se déconnecte)
Précédemment, vous pensiez du point de vue du composant. Lorsque vous regardiez du point de vue du composant, il était tentant de considérer les Effets comme des « rappels » ou des « événements de cycle de vie » qui se déclenchent à un moment précis, comme « après un rendu » ou « avant le démontage ». Cette façon de penser devient très vite compliquée, il est donc préférable de l’éviter.
Au lieu de cela, concentrez-vous toujours sur un seul cycle de démarrage/arrêt à la fois. Peu importe qu’un composant soit en cours de montage, de mise à jour ou de démontage. Tout ce que vous devez faire, c’est décrire comment démarrer la synchronisation et comment l’arrêter. Si vous le faites bien, votre Effet résistera au fait d’être démarré et arrêté autant de fois que nécessaire.
Cela pourrait vous rappeler que vous ne vous demandez pas si un composant est en cours de montage ou de mise à jour lorsque vous écrivez la logique de rendu qui crée le JSX. Vous décrivez ce qui devrait être à l’écran, et Reacts’occupe du reste.
Comment React vérifie que votre Effet peut se resynchroniser
Voici un exemple en direct avec lequel vous pouvez jouer. Cliquez sur « Ouvrir le chat » pour monter le composantChatRoom :
Notez que lorsque le composant est monté pour la première fois, vous voyez trois journaux :
✅ Connexion à la salle "général" sur https://localhost:1234...(développement uniquement)❌ Déconnecté de la salle "général" sur https://localhost:1234.(développement uniquement)✅ Connexion à la salle "général" sur https://localhost:1234...
Les deux premiers logs sont réservés au développement. En développement, React remonte toujours chaque composant une fois.
React vérifie que votre Effet peut se resynchroniser en le forçant à le faire immédiatement en développement.Cela peut vous rappeler le fait d'ouvrir une porte et de la refermer une fois de plus pour vérifier si la serrure fonctionne. React démarre et arrête votre Effet une fois supplémentaire en développement pour vérifierque vous avez bien implémenté son nettoyage.
La principale raison pour laquelle votre Effet se resynchronisera en pratique est si certaines données qu'il utilise ont changé. Dans le bac à sable ci-dessus, changez la salle de discussion sélectionnée. Remarquez comment, lorsque leroomIdchange, votre Effet se resynchronise.
Cependant, il existe aussi des cas plus inhabituels où une resynchronisation est nécessaire. Par exemple, essayez de modifier l'serverUrldans le bac à sable ci-dessus pendant que la discussion est ouverte. Remarquez comment l'Effet se resynchronise en réponse à vos modifications du code. À l'avenir, React pourrait ajouter d'autres fonctionnalités qui reposent sur la resynchronisation.
Comment React sait qu'il doit resynchroniser l'Effet
Vous vous demandez peut-être comment React a su que votre Effet devait se resynchroniser après que leroomIda changé. C'est parce quevous avez dit à Reactque son code dépend duroomIden l'incluant dans laliste des dépendances :
Voici comment cela fonctionne :
- Vous saviez que
roomIdest une prop, ce qui signifie qu'elle peut changer au fil du temps. - Vous saviez que votre Effet lit le
roomId(donc sa logique dépend d'une valeur qui peut changer ultérieurement). - C'est pourquoi vous l'avez spécifiée comme dépendance de votre Effet (pour qu'il se resynchronise lorsque le
roomIdchange).
Chaque fois que votre composant se rend à nouveau, React examinera le tableau des dépendances que vous avez passé. Si l'une des valeurs du tableau est différente de la valeur à la même position que vous avez passée lors du rendu précédent, React resynchronisera votre Effet.
Par exemple, si vous avez passé["general"]lors du rendu initial, et que plus tard vous avez passé["travel"]lors du rendu suivant, React comparera"general"et"travel". Ce sont des valeurs différentes (comparées avecObject.is), donc React resynchronisera votre Effet. D'un autre côté, si votre composant se rend à nouveau mais que leroomIdn'a pas changé, votre Effet restera connecté à la même salle.
Chaque Effet représente un processus de synchronisation distinct
Résistez à l'envie d'ajouter une logique sans rapport à votre Effet simplement parce que cette logique doit s'exécuter en même temps qu'un Effet que vous avez déjà écrit. Par exemple, disons que vous voulez envoyer un événement d'analyse lorsque l'utilisateur visite la salle. Vous avez déjà un Effet qui dépend duroomId, donc vous pourriez être tenté d'ajouter l'appel d'analyse là :
Mais imaginez que vous ajoutiez plus tard une autre dépendance à cet Effet qui nécessite de rétablir la connexion. Si cet Effet se resynchronise, il appellera aussilogVisit(roomId)pour la même salle, ce que vous ne souhaitiez pas. L'enregistrement de la visiteest un processus distinctde la connexion. Écrivez-les comme deux Effets distincts :
Chaque Effet dans votre code doit représenter un processus de synchronisation séparé et indépendant.
Dans l'exemple ci-dessus, supprimer un Effet ne casserait pas la logique de l'autre Effet. C'est une bonne indication qu'ils synchronisent des choses différentes, et donc qu'il était logique de les séparer. D'un autre côté, si vous séparez une logique cohérente en Effets distincts, le code peut paraître "plus propre" mais seraplus difficile à maintenir.C'est pourquoi vous devriez réfléchir à savoir si les processus sont identiques ou distincts, et non pas si le code paraît plus propre.
Les Effets "réagissent" aux valeurs réactives
Votre Effet lit deux variables (serverUrletroomId), mais vous n'avez spécifié que leroomIdcomme dépendance :
PourquoiserverUrln'a-t-il pas besoin d'être une dépendance ?
C'est parce queserverUrlne change jamais à cause d'un nouveau rendu. Il reste toujours le même, peu importe combien de fois le composant est rendu à nouveau et pourquoi. PuisqueserverUrlne change jamais, cela n'aurait pas de sens de le spécifier comme dépendance. Après tout, les dépendances ne font quelque chose que lorsqu'elles changent au fil du temps !
En revanche,roomIdpeut être différent lors d'un nouveau rendu.Les props, l'état et les autres valeurs déclarées à l'intérieur du composant sontréactivescar elles sont calculées pendant le rendu et participent au flux de données de React.
SiserverUrlétait une variable d'état, elle serait réactive. Les valeurs réactives doivent être incluses dans les dépendances :
En incluantserverUrlcomme dépendance, vous assurez que l'Effet se resynchronise après son changement.
Essayez de changer la salle de discussion sélectionnée ou de modifier l'URL du serveur dans ce bac à sable :
Chaque fois que vous changez une valeur réactive commeroomIdouserverUrl, l'Effet se reconnecte au serveur de discussion.
Ce que signifie un Effet avec des dépendances vides
Que se passe-t-il si vous déplacez à la foisserverUrletroomIden dehors du composant ?
Maintenant, le code de votre Effet n'utiliseaucunevaleur réactive, donc ses dépendances peuvent être vides ([]).
En pensant du point de vue du composant, le tableau de dépendances vide[]signifie que cet Effet se connecte à la salle de discussion uniquement lorsque le composant est monté, et se déconnecte uniquement lorsque le composant est démonté. (N'oubliez pas que React leresynchroniserait une fois supplémentaireen développement pour tester votre logique.)
Cependant, si vouspensez du point de vue de l'Effet,vous n'avez pas du tout besoin de penser au montage et au démontage. Ce qui est important, c'est que vous avez spécifié ce que votre Effet fait pour démarrer et arrêter la synchronisation. Aujourd'hui, il n'a pas de dépendances réactives. Mais si vous voulez un jour que l'utilisateur changeroomIdouserverUrlau fil du temps (et qu'ils deviendraient réactifs), le code de votre Effet ne changera pas. Vous devrez simplement les ajouter aux dépendances.
Toutes les variables déclarées dans le corps du composant sont réactives
Les props et l'état ne sont pas les seules valeurs réactives. Les valeurs que vous calculez à partir d'elles sont également réactives. Si les props ou l'état changent, votre composant sera rendu à nouveau, et les valeurs calculées à partir d'elles changeront également. C'est pourquoi toutes les variables du corps du composant utilisées par l'Effet doivent être dans la liste des dépendances de l'Effet.
Imaginons que l'utilisateur puisse choisir un serveur de discussion dans le menu déroulant, mais qu'il puisse également configurer un serveur par défaut dans les paramètres. Supposons que vous ayez déjà placé l'état des paramètres dans uncontexte, donc vous lisez lessettingsà partir de ce contexte. Maintenant, vous calculez l'serverUrlen fonction du serveur sélectionné via les props et du serveur par défaut :
Dans cet exemple,serverUrln'est ni une prop ni une variable d'état. C'est une variable régulière que vous calculez pendant le rendu. Mais comme elle est calculée pendant le rendu, elle peut changer à cause d'un nouveau rendu. C'est pourquoi elle est réactive.
Toutes les valeurs à l'intérieur du composant (y compris les props, l'état et les variables dans le corps de votre composant) sont réactives. Toute valeur réactive peut changer lors d'un nouveau rendu, donc vous devez inclure les valeurs réactives comme dépendances de l'Effet.
En d'autres termes, les Effets "réagissent" à toutes les valeurs provenant du corps du composant.
React vérifie que vous avez spécifié chaque valeur réactive comme dépendance
Si votre linter estconfiguré pour React,il vérifiera que chaque valeur réactive utilisée par le code de votre Effet est déclarée comme sa dépendance. Par exemple, ceci est une erreur de lint carroomIdetserverUrlsont réactifs :
Cela peut ressembler à une erreur de React, mais en réalité React signale un bug dans votre code.roomIdetserverUrlpeuvent changer au fil du temps, mais vous oubliez de resynchroniser votre Effet lorsqu'ils changent. Vous resterez connecté auroomIdet auserverUrlinitiaux même après que l'utilisateur ait choisi différentes valeurs dans l'interface.
Pour corriger le bug, suivez la suggestion du linter en spécifiantroomIdetserverUrlcomme dépendances de votre Effet :
Essayez cette correction dans le bac à sable ci-dessus. Vérifiez que l'erreur du linter a disparu et que le chat se reconnecte quand c'est nécessaire.
Note
Dans certains cas, Reactsaitqu'une valeur ne change jamais même si elle est déclarée à l'intérieur du composant. Par exemple, la fonctionsetretournée paruseStateet l'objet ref retourné paruseRefsontstables—ils sont garantis de ne pas changer lors d'un nouveau rendu. Les valeurs stables ne sont pas réactives, donc vous pouvez les omettre de la liste. Les inclure est autorisé : elles ne changeront pas, donc cela n'a pas d'importance.
Que faire quand vous ne voulez pas resynchroniser
Dans l'exemple précédent, vous avez corrigé l'erreur de lint en listantroomIdetserverUrlcomme dépendances.
Cependant, vous pourriez plutôt "prouver" au linter que ces valeurs ne sont pas des valeurs réactives,c'est-à-dire qu'ellesne peuvent paschanger à la suite d'un nouveau rendu. Par exemple, siserverUrletroomIdne dépendent pas du rendu et ont toujours les mêmes valeurs, vous pouvez les déplacer en dehors du composant. Maintenant, elles n'ont pas besoin d'être des dépendances :
Vous pouvez également les déplacerà l'intérieur de l'Effet.Ils ne sont pas calculés pendant le rendu, donc ils ne sont pas réactifs :
Les Effets sont des blocs de code réactifs.Ils se resynchronisent lorsque les valeurs que vous lisez à l'intérieur changent. Contrairement aux gestionnaires d'événements, qui ne s'exécutent qu'une fois par interaction, les Effets s'exécutent chaque fois qu'une synchronisation est nécessaire.
Vous ne pouvez pas « choisir » vos dépendances.Vos dépendances doivent inclure chaquevaleur réactiveque vous lisez dans l'Effet. Le linter impose cette règle. Parfois, cela peut entraîner des problèmes comme des boucles infinies et une resynchronisation trop fréquente de votre Effet. Ne corrigez pas ces problèmes en supprimant le linter ! Voici ce qu'il faut essayer à la place :
- Vérifiez que votre Effet représente un processus de synchronisation indépendant.Si votre Effet ne synchronise rien,il est peut-être inutile.S'il synchronise plusieurs choses indépendantes,divisez-le.
- Si vous souhaitez lire la dernière valeur d'une prop ou d'un état sans « réagir » à celle-ci et resynchroniser l'Effet,vous pouvez diviser votre Effet en une partie réactive (que vous garderez dans l'Effet) et une partie non réactive (que vous extrairez dans quelque chose appelé unÉvénement d'Effet).Lisez la séparation des Événements et des Effets.
- Évitez de dépendre d'objets et de fonctions comme dépendances.Si vous créez des objets et des fonctions pendant le rendu et que vous les lisez ensuite depuis un Effet, ils seront différents à chaque rendu. Cela entraînera une resynchronisation de votre Effet à chaque fois.En savoir plus sur la suppression des dépendances inutiles des Effets.
Piège
Le linter est votre ami, mais ses pouvoirs sont limités. Le linter sait seulement quand les dépendances sontfausses. Il ne connaît pasla meilleurefaçon de résoudre chaque cas. Si le linter suggère une dépendance, mais que son ajout provoque une boucle, cela ne signifie pas qu'il faut ignorer le linter. Vous devez modifier le code à l'intérieur (ou à l'extérieur) de l'Effet pour que cette valeur ne soit pas réactive et n'ait pasbesoind'être une dépendance.
Si vous avez une base de code existante, vous pourriez avoir des Effets qui suppriment le linter comme ceci :
Sur lesprochainespages, vous apprendrez comment corriger ce code sans enfreindre les règles. Cela vaut toujours la peine d'être corrigé !
Récapitulatif
- Les composants peuvent être montés, mis à jour et démontés.
- Chaque Effet a un cycle de vie distinct du composant qui l'entoure.
- Chaque Effet décrit un processus de synchronisation distinct qui peutdémarrerets'arrêter.
- Lorsque vous écrivez et lisez des Effets, pensez du point de vue de chaque Effet individuel (comment démarrer et arrêter la synchronisation) plutôt que du point de vue du composant (comment il est monté, mis à jour ou démonté).
- Les valeurs déclarées dans le corps du composant sont « réactives ».
- Les valeurs réactives doivent resynchroniser l'Effet car elles peuvent changer au fil du temps.
- Le linter vérifie que toutes les valeurs réactives utilisées à l'intérieur de l'Effet sont spécifiées comme dépendances.
- Toutes les erreurs signalées par le linter sont légitimes. Il existe toujours un moyen de corriger le code sans enfreindre les règles.
Try out some challenges
Challenge 1 of 5:Fix reconnecting on every keystroke #
In this example, the ChatRoom component connects to the chat room when the component mounts, disconnects when it unmounts, and reconnects when you select a different chat room. This behavior is correct, so you need to keep it working.
However, there is a problem. Whenever you type into the message box input at the bottom, ChatRoom also reconnects to the chat. (You can notice this by clearing the console and typing into the input.) Fix the issue so that this doesn’t happen.
