Archives Wild Code School
React - Class Components

React Class Components 03.2 - Life Cycle Deepening

Quête 4 sur 5 · 1 image archivée

Introduction

Dans les quêtes précédentes, tu as découvert componentDidMount, l'une des méthodes de cycle de vie les plus utilisées.

L'un de ses cas d'usage est le lancement de requêtes API quand un composant est ajouté pour la première fois à une page. Mais le lifecycle (cycle de vie du composant) va bien plus loin !

C'est parti ! 🚀

image

🤓 À la fin de ce cours, tu seras capable de :

✅ Avoir une vue d'ensemble du cycle de vie d'un composant. ✅ Comprendre componentWillUnmount. ✅ Comprendre componentDidUpdate. ✅ Utiliser componentDidUpdate avec React Router.

Vue d'ensemble

Mosh Hamedani compare les méthodes de cycle de vie à "des événements qui se produisent depuis la naissance d'un composant React jusqu'à sa mort".

En suivant cette métaphore :

  • componentDidMount est appelée juste après la "naissance" d'un composant,
  • componentDidUpdate fournit une réponse à toute mise à jour des props/state du composant, pendant sa durée de vie,
  • componentWillUnmount est la méthode "symétrique" de componentDidMount. Elle te permet d'exécuter du code juste avant qu'un composant ne soit retiré du DOM, c'est-à-dire avant qu'il ne "meure".

Dans cette quête, tu vas en apprendre plus sur ces deux dernières méthodes, qui peuvent servir à résoudre les problèmes les plus courants.

Cette quête s'appuie sur des ressources (documentation officielle, articles de blog) que tu dois lire attentivement. L'idée est de t'habituer à lire de la documentation en anglais. Si cet exercice te paraît difficile au début, n'hésite pas à travailler avec un ou une camarade sur la section componentDidUpdate.

Cette application interactive te montre les principales étapes du cycle de vie :

  • Le montage (Mounting, la naissance)
  • La mise à jour (Update, la croissance)
  • Le démontage (Dismantling, la mort)

Ce qu'il est important de comprendre dans ce diagramme, c'est ceci :

  • Il existe de nombreuses méthodes de cycle de vie au-delà de componentDidMount, componentDidUpdate et componentWillUnmount. Cependant, connaître ces trois-là devrait suffire à couvrir la plupart des cas d'usage.
  • Mettre à jour les props d'un composant (depuis son composant parent) ou appeler setState déclenchera un nouvel appel à render, suivi de la méthode componentDidUpdate.

Important : Si tu cherches "react lifecycle" sur internet, tu as de grandes chances de tomber sur des ressources qui mentionnent les méthodes componentWillMount, componentWillUpdate et componentWillReceiveProps. Ces méthodes sont désormais deprecated (dépréciées, déconseillées) depuis React 16.3 (mars 2018) : en cas de doute, essaie de toujours te référer à la documentation officielle.

↗
Diagramme des méthodes de cycle de vie React - GitHub

Dépôt GitHub

github.com
↗
Diagramme des méthodes de cycle de vie React - En ligne

Version en ligne

projects.wojtekmaj.pl

Symétrie entre le montage et le démontage

componentDidMount te permet d'initialiser des choses juste après le premier render d'un composant. À l'inverse, componentWillUnmount nous permet de nettoyer toute opération en cours juste avant qu'il ne soit retiré de la page.

Un très bon exemple est donné par la documentation officielle, dans la section Adding Lifecycle Methods to a Class.

Les timers en JavaScript

Les timers (minuteurs) sont vraiment utiles, alors lis bien toute cette section, y compris la documentation DND sur setInterval (sections "Syntax" et "Example"). Note que setInterval est une fonction built-in (native, intégrée au langage) de JavaScript et n'est pas spécifique à React.

Pour te permettre de mieux comprendre, voici un exemple de setInterval sur CodePen.

codepen
https://codepen.io/wildcodeschool/pen/QWWqLxe

Au chargement du HTML, la fonction startFlashText est appelée et démarre un timer à l'aide de setInterval. On passe la fonction changeColour à setInterval, qui sera appelée automatiquement toutes les secondes (1000 millisecondes) comme indiqué dans le 2e argument de setInterval. Cette fonction va faire basculer la couleur du texte du rouge au bleu.

La valeur de retour de setInterval est stockée dans intervalID. Elle identifie le timer, afin de pouvoir l'arrêter plus tard quand il n'est plus nécessaire. Appuyer sur le bouton "Stop" déclenche l'appel de stopFlashText qui arrête le timer en effaçant l'intervalle avec clearInterval, auquel on passe l'intervalID.

Utilisation avec React

Comme expliqué dans cette quête, une fois que l'horloge Clock est initialisée avec une date initiale dans le state, on peut alors démarrer un timer à l'intérieur de componentDidMount, qui sera arrêté à l'intérieur de componentWillUnmount.

Ce timer mettra à jour la date du state à intervalles réguliers. Arrêter le timer quand le composant est retiré de la page libère des ressources et est une bonne pratique (pourquoi mettre à jour quelque chose qui n'est plus affiché ?).

Malheureusement, l'exemple de la documentation ne montre pas précisément ce qui se passe quand le composant Clock est retiré de la page.

Cet exemple légèrement différent va te permettre de mieux visualiser l'ordre dans lequel les méthodes de cycle de vie sont appelées.

codepen
https://codepen.io/wildcodeschool/pen/OJJxJWL?editors=0010

Le composant Clock est un enfant d'App, et App contient une valeur booléenne showClock qui indique si Clock doit être affiché ou non.

Voyons ce qui apparaît dans la console du navigateur quand tu appuies sur le bouton "Show clock", puis une autre fois après avoir caché l'horloge quelques secondes plus tard.

Un autre cas d'usage de componentDidMount et componentWillUnmount pourrait être l'écoute d'événements spécifiques en utilisant addEventListener dans componentDidMount et removeEventListener dans componentWillUnmount.

Mises à jour des props et du state

Cette étape porte sur componentDidUpdate et va faire ressurgir quelques souvenirs de componentDidMount et de React Router !

Prenons l'exemple d'un blog. Avec React Router, tu peux déclarer une route dont le composant affiche le contenu complet d'un article. L'identifiant de l'article peut être passé via un paramètre d'URL.

Quand tu affiches le composant pour la première fois, tu peux charger les données de l'article avec axios/fetch dans componentDidMount et mettre à jour le state en conséquence. Mais que se passe-t-il si l'utilisateur clique sur un lien vers un autre article ? Il sera toujours sur la même route mais avec un paramètre d'URL différent (par exemple en passant de /article/1 à /article/2). La page ne sera pas mise à jour, si tu ne récupères pas les données de cet autre article depuis componentDidUpdate.

Ce problème et la façon de le résoudre sont bien décrits dans cet article : A Practical Use Case for React's componentDidUpdate.

Tu n'as pas forcément besoin de recopier les exemples de code, car l'auteur nous a donné des liens CodeSandbox à consulter :

  • Un premier montrant l'application de blog dans son état initial, avec le problème de mise à jour.
codesandbox
https://codesandbox.io/s/r1ywq3kkmn
  • Un autre montrant la solution au problème.
hidden
See solution!|||codesandbox||||||0|||Close solution
https://codesandbox.io/s/x729mr1qjw

Quelques notes pour guider ta lecture :

  • Prends le temps de regarder le code des composants : App, Home et PostDetails.

  • Dans Home, une liste de Link est générée à partir d'une liste d'éléments (posts), qui sont écrits dans un fichier séparé. Chaque Link contient l'id d'un article comme paramètre d'URL.

  • Dans PostDetails, on récupère l'id depuis l'URL pour ensuite charger l'article demandé. Pour cela, on utilise fetchpostData avec cet id dans componentDidMount).

  • Toujours dans PostDetails, fetchPostData "simule" le chargement d'un article. Au lieu d'interroger un article avec Fetch ou Axios, on récupère un article depuis la liste posts précédente. Cela se fait avec la méthode Array.find(...) qui fonctionne de la même manière que Array.filter : une fonction est passée en paramètre pour déterminer si un élément répond à un certain critère. Contrairement à filter qui garde tous les éléments correspondant au critère, find ne renvoie que le premier élément correspondant (ou undefined s'il n'y a pas de correspondance).

  • Toujours dans PostDetails, la solution au problème de rechargement des données est fournie à l'aide de componentDidUpdate. La méthode de cycle de vie componentDidUpdate reçoit les props précédentes comme premier argument. L'idée est de récupérer la valeur actuelle de this.props.postId et de la comparer à l'ancienne valeur de prevProps.postId. Si elles sont différentes, on doit appeler fetchPostData pour charger le nouvel article.

↗
ESSENTIEL - componentDidUpdate
reactjs.org
↗
ESSENTIEL - Array.prototype.find
developer.mozilla.org
↗
ESSENTIEL - Un cas d'usage concret de componentDidUpdate en React
kevgathuku.me

💪 Challenge

Utiliser axios depuis componentDidUpdate

Ce challenge fait suite à l'article sur componentDidUpdate, en simulant un appel API avec fetchPostData.

On te donne maintenant une application dont la structure est très similaire. Ouvre ce CodeSandbox et forke-le (fork, crée ta propre copie modifiable du projet).

Quelques indications sur la structure :

  • Il y a deux routes dans App
    • Une menant à la page d'accueil qui affiche le composant Home, lequel affiche une liste de personnages depuis CharacterList.
    • Une autre affichant les détails d'un personnage via le composant CharacterDetails.
  • Dans son état initial, CharacterDetails ne charge aucune donnée.
  • Ton objectif est de faire en sorte que CharacterDetails charge correctement les données :
    • Ajoute le code manquant (requête API avec axios) dans loadCharacterDetails pour charger les détails du personnage (l'url t'est déjà fournie), et mets à jour les données du state (character) en conséquence avec les données reçues.
    • En t'appuyant sur ce qui est montré dans l'article de la quête, ajoute du code pour détecter le changement de paramètre d'URL. Chaque fois que characterId change, tu lanceras une nouvelle requête pour charger les données de ce personnage (correspondant à la nouvelle valeur de characterId dans l'URL).

Poste l'URL de ton fork CodeSandbox comme solution.

🧐 Critères de validation

  • [ ] Les données d'un personnage sont chargées automatiquement quand tu cliques sur un lien depuis la page d'accueil.
  • [ ] Les données sont rechargées quand tu cliques sur un lien vers un autre personnage depuis la page de détails.

Introduction

In the previous quests, you were introduced to componentDidMount, one of the most widely used lifecycle methods.

One of its use cases is the launch of API requests when a component is first added to a page. But there is more to lifecycle!

Let's get it started! 🚀

image

🤓 By the end of this lecture you will be able to:

✅ Get an overview of a component lifecycle. ✅ Understand componentWillUnmount. ✅ Understand componentDidUpdate. ✅ Use componentDidUpdate with React Router.

Overview

Mosh Hamedani compares lifecycle methods to "events that occur from the birth of a React component until its death".

Following this metaphor:

  • componentDidMount is called just after the "birth" of a component,
  • componentDidUpdate provides a response to any component props/state updates, during the component lifetime,
  • componentWillUnmount the "symmetrical" of componentDidMount. It allows you to run code just before a component is removed from the DOM, i.e. before it "dies".

In this quest you will find out more about these two last methods, which can be used to solve the most common problems.

This quest is based on resources (official documentation, blog articles) that you should read carefully. The idea is to get used to reading documentation in English. If this exercise proves to be difficult in the beginning, do not hesitate to work with a classmate on the componentDidUpdate section.

This interactive application shows you the main stages of the lifecycle:

  • Mounting (birth)
  • Update (growth)
  • Dismantling (death)

What is important to understand from this diagram is the following:

  • There are many lifecycle methods beyond componentDidMount, componentDidUpdate and componentWillUnmount. However, knowing these three should be enough to cover most use cases.
  • Updating a component props (from its parent component) or calling setState will result into a new render call, followed by the componentDidUpdate method.

Important: If you look for "react lifecycle" on the internet, you are likely to find resources mentioning componentWillMount, componentWillUpdate and componentWillReceiveProps methods. These methods are now deprecated since React 16.3 (March 2018): when in doubt, try to always refer to the official documentation.

↗
React Lifecycle Methods diagram - GitHub

Repo GitHub

github.com
↗
React Lifecycle Methods diagram - Online

Online version

projects.wojtekmaj.pl

Symmetry between Mounting and Unmounting

componentDidMount allows you to initialize things right after a component's first render. On the other hand, componentWillUnmount allows us to clean any ongoing operation just before it is removed from the page.

A very good example is given by the official documentation, in the section Adding Lifecycle Methods to a Class.

Timers in JavaScript

Timers are really useful, so be sure to read this entire section, including the DND documentation on setInterval ("Syntax" and "Example" sections). Note that setInterval is a JavaScript built-in function and is not specific to React.

To make it easier for you to understand, here is a setInterval example on CodePen.

codepen
https://codepen.io/wildcodeschool/pen/QWWqLxe

When loading HTML, the startFlashText function is called and starts a timer using setInterval. We pass the function changeColour to setInterval which will be called automatically every second (1000 milliseconds) as mentioned in the 2nd argument of setInterval. This function will toggle the text color from red to blue.

The return value of setInterval is stored in intervalID. It identifies the timer, so that it can be stopped later one when it is no longer needed. Pressing the "Stop" button triggers the call of stopFlashText which stops the timer by clearing the interval using clearInterval to which the intervalID is passed.

Use with React

As explained in this quest, once the Clock is initialized with an initial date in the state, we then can start a timer inside componentDidMount and which will be cleared inside componentWillUnmount.

This timer will update the date from the state at regular intervals. Stopping the timer when the component is removed from the page frees up resources and is good practice (why would you update something that isn't shown anymore?).

Unfortunately, the documentation example does not show precisely what happens when the Clock component is removed from the page.

This slightly different example will allow you to visualize a little better the order in which lifecycle methods are called.

codepen
https://codepen.io/wildcodeschool/pen/OJJxJWL?editors=0010

The Clock component is a child of App, and App contains a boolean value showClock which indicates whether or not Clock should be displayed.

Let's see what shows up in the browser console when you press the "Show clock" button, and another time after hiding the clock a few seconds later.

Another use case of componentDidMount and componentWillUnmount could be for listening to specific events using addEventListener in componentDidMount and removeEventListener in componentWillUnmount.

Props and State Updates

This step is about componentDidUpdate and will bring back some memories from componentDidMount and React Router!

Let's pick an example of a blog. Using React Router, you can declare a route which component displays the complete content of an article. The article ID can be passed using a URL parameter.

When you show the component for the first time, you can load the material data using axios/fetch in componentDidMount and update the state accordingly. But what happens if the user clicks on a link to another article? They will still be on the same route but with a different URL parameter (e.g. going from /article/1 to /article/2). The page will not be updated, if you don't fetch the data for that other article from componentDidUpdate.

This problem and how to solve it is well described in this article: A Practical Use Case for React's componentDidUpdate.

You don't necessarily need to copy the code samples, because the author gave us CodeSandboxes links to look at:

  • A first one showing the blog application in its initial state, with the update problem.
codesandbox
https://codesandbox.io/s/r1ywq3kkmn
  • Another one showing the solution to the problem.
hidden
See solution!|||codesandbox||||||0|||Close solution
https://codesandbox.io/s/x729mr1qjw

A few notes to guide your reading:

  • Take the time to look at the components code: App, Home and PostDetails.

  • In Home, a list of Links is generated from a list of items (posts), which are written in a separated file. Each Link contains an article id as a URL parameter .

  • In PostDetails, we get the id from the URL to then load the requested article. To do so, they use fetchpostData with that id in componentDidMount).

  • Also in PostDetails fetchPostData "simulates" the loading of an article. Instead of querying an article using Fetch or Axios, we get an article from the previous posts list. This is done using the Array.find(...) method which works in the same way as in Array.filter, a function is passed as a parameter to determine whether an element passes a certain criterion. Unlike filter which keeps all the elements corresponding to the criterion, find only returns the first matching element (or undefined if there's no match).

  • Still in PostDetails, the solution to the data reloading problem is provided using componentDidUpdate. The componentDidUpdate lifecycle method takes in the previous props as the first argument. The idea is to get the current value of this.props.postId and compare it to the old value from prevProps.postId. If they're different, we need to call fetchPostData to load the new article.

↗
ESSENTIAL - componentDidUpdate
reactjs.org
↗
ESSENTIAL - Array.prototype.find
developer.mozilla.org
↗
ESSENTIAL - A Practical Use Case for React's componentDidUpdate
kevgathuku.me

💪 Challenge

Using axios from componentDidUpdate

This challenge follows on from the article about componentDidUpdate, simulating an API call using fetchPostData.

You are now given an application whose structure is very similar. Open this CodeSandbox and fork it.

Some indications about the structure:

  • There are two routes in App
    • One leading to the homepage showing the Home component, which displays a list of characters from CharacterList.
    • Another one showing a character's details using the CharacterDetails component.
  • In its initial state, CharacterDetails does not load any data.
  • Your goal is to make sure CharacterDetails loads the data correctly:
    • Add missing code (API request using axios) in the loadCharacterDetails to load the character's details (the url is already provided to you), and update the state data (character) accordingly with the data received.
    • Based on what is shown in the quest article, add code to detect the change of URL parameter. Whenever characterId changes, you will run a new request to load the data for that character (corresponding to the new characterId value in the URL).

Post the URL of your CodeSandbox fork as a solution.

🧐 Validation criterias

  • [ ] A character's data is loaded automatically when you click on a link from the homepage.
  • [ ] The data is reloaded when you click on a link to another character from the details page.

Ressources partagées sur cette quête