REACT - useEffect : Comment ressusciter les méthodes du cycle de vie ?
Tags: react, french, frontend, hook
Published: 2023-03-29 21:27:04

Le hook d’effet ou **useEffect** fait partie des hooks les plus utilisés. Il permet d’exécuter des effets de bord une fois que le composant est monté dans la vue. Nous verrons comment useEffect est une solution permettant de reproduire les méthodes de **cycle de vie** d’un composant.

&nbsp;
# Class component

Pour comprendre correctement ce hook, je vous propose de faire un voyage dans le temps et de revenir avant la **version 16.8** de React. Durant l’époque pré-hook, les functionnal components n’existait pas. Les class components étaient implémentés avec une toute autre logique.

&nbsp;
## Méthode de cycle de vie d’un class component

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8jxepq34tpdb7qxrqz6c.jpg)

Je vous propose de parcourir les trois méthodes principales du **cycle de vie** des class components de React.

&nbsp;
### 🔁 1 - componentDidMount

Cette méthode du cycle de vie est appelée juste après la méthode **render qui** permet de transformer le code React en un élément du DOM, c’est ce qu’on appelle la phase de **mount**. 

A cette étape, on se trouve juste à la création du composant et l’on peut faire des opérations d’initialisation tel que les **appels réseau** ou la **gestion des states**.

&nbsp;
### 🔁 2 - componentDidUpdate

Cette méthode du cycle de vie est appelée juste après qu’un composant soit **mis à jour** dans le DOM. En React, la mise à jour du composant est principalement régit par le changement de ses **states** et de ses **props**. 

La méthode **componentDidUpdate** prend en entrée deux paramètres :

- **prevProps** : les anciennes props du composant
- **prevState** : les anciens states du composant

Grâce à cette méthode, il est possible de détecter si une props ou un state a changé et faire un traitement en conséquence.

&nbsp;
### 🔁 3 - componentWillUnmount

Cette méthode de cycle de vie est appelée juste avant que le composant soit **unmount** c’est à dire supprimé du DOM.

Cette méthode sert à **nettoyer la mémoire** et faire de l’**optimisation** en annulant toutes les suscriptions ou appel réseau en cours afin d’éviter d’avoir de la data zombie dans l’application.

&nbsp;
# Functionnal Component

Bienvenue de nouveau dans les temps modernes et l’époque des hooks ! Le functionnal component fait son apparition lors de la **v16.8** de React (en février 2019). Ce changement apporte une nouvelle façon de programmer en React en remplaçant la **POO** (**P**rogrammation **O**rientée **O**bjet) par le **PF** (**P**rogrammation **F**onctionnelle). 

Lors de ce changement, React a du trouver une alternative pour les **méthodes de cycle de vie** du **functionnal component**. La solution qui a été retenu est l’utilisation de **useEffect**.

Regardons de plus prêt ce qu’est **useEffect** puis comment retranscrire les **méthodes de cycle** de vie vu ci-dessus avec ce hook.

&nbsp;
## Fonctionnement du hook useEffect

Avant de se lancer dans l’utilisation de useEffect, prenons deux minutes pour analyser ce hook. Voici la signature de **useEffect** dans l’API React.

```tsx
function useEffect(setup, dependencies?): void;
```

&nbsp;
### Paramètres :

- **setup** : fonction callback qui est exécutée à chaque fois qu’une valeur du tableau des dépendances change. Cette fonction peut retourner une callback appelé fonction de **clean up** qui sera exécutée avant le trigger du prochain useEffect ou lorsque le composant est démonté.
- **dependencies** (optionnal) : ce tableau est constitué d’objets qui sont des **states** et **props** utilisé dans le useEffect. Lorsque ces objets changent, React exécute la fonction callback setup. Nous verrons comment manipuler ce tableau dans la suite de l’article.

Il est possible de se représenter mentalement le useEffect comme cela : un effet qui est **exécuté** à chaque fois qu’une **dépendance change**. La fonction clean up est exécutée avant chaque exécution du useEffect. Nous verrons dans la suite les détails concernant ce comportement.

```tsx
useEffect(() => {
	effect 
	return () => {
		cleanup
	}
}, [dependencies])
```

Maintenant que nous savons à quoi ressemble un useEffect, voyons à quoi il sert en pratique.

&nbsp;
## Exemple

Pour illustrer les propos de cet article, nous prendrons l’exemple du composant <**Glow** color /> que vous pouvez retrouver sur le repository git suivant : https://github.com/Delmotte-Vincent/demo-useEffect. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/boi37h7xbxpgnr4vywcl.gif)

Ce composant a une props **color** et une variable d’état **lightOn** qui indique si la lumière est allumée ou éteinte. Ce composant permet de modifier la couleur de la lumière et d’allumer ou éteindre l’ampoule.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hqosgb2025lctfvl60og.png)

&nbsp;
## Simuler le cycle de vie avec useEffect

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwfhjf75e93wc4kx8ile.jpg)

L’une des missions de useEffect est de **simuler le cycle de vie d’un functionnal component**. Dans cette partie, je vais vous montrer l’**équivalence** des trois méthodes de cycle de vie d’un class component implémenté dans un **functionnal component** avec le hook **useEffect**.

&nbsp;
### 🔁 1 - Equivalent de componentDidMount

Pour reproduire le comportement de la méthode **componentDidMount** (exécuter du code au mount du composant) nous utilisons un comportement du useEffect qui est : **chaque fois qu’un composant est mount, quoi qu’il se passe le useEffect est exécuté une première fois**. Ensuite, pour ne pas l’exécuter de nouveaux et ainsi obtenir le comportement de **componentDidMount**, il suffit de placer un **tableau de dépendances vide**.

```tsx
// componentDidMount
useEffect(() => {
	console.log(`👋 Trigger onMount`);
}, []);
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5fi7vd6nc57e4qwike1.gif)

&nbsp;
### 🔁 2 - Equivalent de componentDidUpdate

Il existe plusieurs façon de reproduire le comportement de la méthode **componentDidUpdate** dans un **functionnal component**. 

La première façon, est **déconseillée** mais existe donc nous l’abordons. Cela consiste à omettre le deuxième paramètre du useEffect. Si un useEffect ne possèdent pas de tableau de dépendance, il sera exécuté à chaque modification du composant, c’est à dire à chaque changement de **props** ou de **state**.

```tsx
// componentDidUpdate
useEffect(() => {
	console.log(`👋 Trigger on every changement (⚠️ not recommended)`);
});
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hcmn26tnw9cr6py2h5wj.gif)

⚠️ **Cette façon de faire est à éviter au maximum** car nous avons très peu de contrôle sur ce qu’on fait et donc cela détériore les performances de l’application.

La deuxième façon de reproduire la méthode **componentDidUpdate** est de passer le tableau avec la **liste des dépendances** pour laquelle le useEffect doit être exécuté. 

Prenons comme exemple la situation suivante : vous voulez afficher la variable d’état lightOn à chaque fois que cette dernière change de valeur. Pour cela c’est simple, il suffit de passer dans le tableau des dépendances la variable lightOn.

```tsx
// componentDidUpdate on state lightOn changes
useEffect(() => {
	console.log(`✅ Effect => lightOn : ${lightOn ? "💡" : "⚫"}`);
}, [lightOn]);
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c160p2375x6eqju1rik5.gif)

On remarque que la callback du useEffect est appelé au **mount** du composant puis seulement lorsque **lightOn change de valeur**, si color change de valeur il n’y aura aucun message affiché dans la console.

Maintenant faisons la même chose et affichons la valeur de la **props color** passée au composant à chaque fois que cette dernière change.

```jsx
// componentDidUpdate on props color changes
useEffect(() => {
	console.log(`✅ Effect => color : ${getHeartColor()}`);
}, [color]);
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1bjt0y843iao1q4qtjy0.gif)

De même ici, seul le changement de la props color entraine l’exécution de la callback.

Pour finir avec la méthode **componentDidUpdate** je vais vous montrer la combinaison des deux exemples suivants, c’est à dire, exécuter la callback sous deux conditions qui sont :

- le state lightOn change de valeur
- la props color change de valeur

```jsx
// componentDidUpdate on state lightOn and props color change
useEffect(() => {
	console.log(`✅ Effect => color : ${getHeartColor()} | lightOn : ${lightOn ? "💡" : "⚫"}`);
}, [lightOn, color]);
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8h8w7zr37c5j8cskxo3z.gif)

On a donc bien le message qui affiche les valeurs de lightOn et de color chaque fois que l’une d’entre elles changent de valeur.

&nbsp;
### 🔁 3 - Equivalent de componentWillUnmount

Pour reproduire la méthode **componentWillUnmount** dans un functionnal component, vous devez passez une **fonction de nettoyage** que le hook useEffect appellera lorsque le composant sera **unMount**. Cette fonction clean up est retournée par la fonction callback du useEffect.

Le code suivant permet d’appliquer la fonction de callback lorsque le composant est unmount.

```tsx
// clean up
useEffect(() => {
return () => console.log(`❌ Clean up`);
}, []);
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i6jl28gnr26cteb4ontd.gif)

On remarque qu’à chaque fois que le composant est unmount, la fonction de clean up est exécutée. Ainsi, grâce à ce comportement, on peut **annuler tous les effets qui ne sont plus nécessaires lorsque le composant est unmount** (on peut penser à un appel réseau par exemple).

⚠️  **Attention, ici il y a un prank !** Je dois vous avouer quelque chose. Je ne vous ai pas tout dit sur la fonction de clean up. Regardons l’exemple suivant : 

```jsx
// clean up with on state lightOn and props color change
useEffect(() => {
	console.log(`✅ Effect => color : ${getHeartColor()} | lightOn : ${lightOn ? "💡" : "⚫"}`);
	return () => console.log(`❌ Clean up`);
}, [lightOn, color]);
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tl7cv1aywm89n23glaeq.gif)

Et la vous vous dites : “La fonction de clean up n’est pas censée s’exécuter seulement lorsque le composant est unmount ? Pourquoi est-elle exécutée chaque fois qu’une valeur du tableau de dépendances changes ?”

Oui, vous ne rêvez pas, il est possible d’exécuter la fonction de **clean up** chaque fois que la valeur d’une des **dépendances change**. Pour comprendre ce fonctionnement je dois vous expliquer ce qui se passe lorsque qu’un state ou une props change.

Que ce passe-t-il en réalité lorsque la props color change? 

| Ordre | Action |
| --- | --- |
| 1 | React détecte le changement de valeur de la props color. |
| 2 | React déclenche la fonction de clean up de tous les useEffect possédant color dans leur tableau de dépendances. |
| 3 | React procèdent à la modification du composant en mettant à jour la valeur de color, et il trigger tous les useEffect possédant color dans leur tableau de dépendances. |

Cette stratégie adoptée par React permet de nettoyer (clean up) tous les effets entre **chaque rendu**, laissant au développeur la possibilité d’annuler des traitements asynchrones non complétés (ex : une requête réseau). Ce processus permet d’éviter les données zombies qui peuvent potentiellement ralentir l’application.

Pour conclure, la fonction clean up permet de réaliser le rôle de la méthode de cycle de vie **componentWillUnmount** mais elle a également le pouvoir d’appliquer un traitement entre chaque changement de valeur du tableau de dépendances du useEffect. On peut dire que la fonction de clean up est une méthode componentWillUnmount sous stéroïde.

&nbsp;
# Conclusion

Nous avons vu dans cet article un historique de la façon de faire des **composants** en React, en passant des class components aux functionnal components. 

Nous avons vu qu’une des principales missions du **useEffect** est de reproduire le comportement des **méthodes du cycle de vie** qui étaient présentes dans les class components et qui ont disparus avec l’avènement des functionnal components.

J’espère vous avoir éclairé sur ce hook qui est souvent utilisé en React mais qui recèle beaucoup de secret. J’espère que cet article vous a plus et vous aidera dans vos prochains défis de code !