React Intermédiaire 01bis - useEffect
Introduction
Le hook d'effet te permet d'exécuter des effets de bord dans les composants fonctionnels. Voyons plus en détail ce que cela signifie et comment l'utiliser.

🤓 A la fin de cette quête, tu comprendras :
- ✅ Ce qu'est le hook d'effet
- ✅ Comment utiliser le hook d'effet en phase de montage
- ✅ Comment utiliser le hook d'effet pendant la phase de mise à jour
- ✅ Comment utiliser le hook d'effet lors de la phase de démontage
🤷🏻♂️ Qu'est-ce que useEffect ?
Le hook d'effet te permet d'exécuter des effets de bord dans des composants fonctionnels.
Voici à quoi ça ressemble :
useEffect(() => {
console.log('Hello World');
},[]);
Définition:
Les "effets de bord" font référence à la modification de variables en dehors de l'instance, les opérations asynchrones, etc...
useEffect permet d'exécuter du code sans rapport avec le rendu tout au long du cycle de vie d'un composant.
Tu dois d'abord connaître les trois phases principales de ce cycle de vie :
🏗️ Phase de montage - Naissance du composant
C'est lorsque le composant est monté sur le DOM avec tous ses composants enfants. C'est un bon moment pour déclencher des appels d'API ou pour déclencher des fonctions une fois que tous les composants sont chargés à l'écran.
♻️ Phase de mise à jour - Évolution du composant
Cette phase se produit lorsque le composant reçoit des modifications (nouvelles valeurs pour les props, état mis à jour...). Au cours de cette phase, tu dois surveiller toute modification apportée au composant et déclencher les effets secondaires associés (par exemple, rappelez ton API).
💥 Phase de démontage - Fin du composant
C'est le moment où le composant est démonté, enlevé du DOM. C'est la phase où tu dois faire un nettoyage sur ton composant (par exemple, arrêter les timers, annuler les abonnements...).
💪 Jouons avec le hook d'effet !
useEffect est une fonction qui accepte deux paramètres :
React.useEffect(() => {}, [])
Le premier est une fonction de rappel (callback) et le second est un tableau des dépendances.
useEffect exécutera le callback :
- Pendant la phase de montage : le callback passé en premier argument de
useEffectsera toujours exécuté lorsque le composant est monté. - Lors de la phase de mise à jour : si aucun tableau de dépendances n'est spécifié ou si un élément présent dans le tableau des dépendances a changé (cas d'un tableau vide, rien ne se passera pendant la phase de mise à jour).
- Pendant la phase de démontage : si le callback retourne une fonction
Ne t'inquiéte pas si ce n'est pas clair à 100% pour le moment : nous allons passer en revue des exemples qui t'aideront à comprendre.
🏗️ Phase de montage
La phase de montage d'un composant est la phase qui se produit lorsque le composant est monté sur le DOM.
⏲️ Prends quelques minutes pour regarder cette application et essayer de comprendre ce qui se passe ici.
import React from "react";
export default function App() {
React.useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users/1')
.then(response => response.json())
.then(data => console.log(data));
}, []);
return <div className="App" />;
}
Dans cet exemple, nous utilisons useEffect avec :
- Une fonction de rappel qui contient un fetch
- Un tableau des dépendances vide
Étant donné que nous spécifions un tableau des dépendances vide, le rappel ne s'exécutera que pendant la phase de montage.
Tableau des dépendances vide = le callback de useEffect ne s'exécute qu'une seule fois (pendant la phase de montage)
♻️ Phase de mise à jour
La phase de mise à jour se produit lorsque un état ou une prop d'un composant change et, par conséquent, qu'un nouveau rendu a lieu.
⏲️ Prends quelques minutes pour regarder cette application et essayer de comprendre ce qui se passe ici. Essaie de jouer avec l'interface utilisateur et regarde également ce qui se passe dans la console.
import React from "react";
export default function App() {
const [count, setCount] = React.useState(0);
const [name, setName] = React.useState("");
console.log('Component re-render');
React.useEffect(() => {
console.log(`Youhou ! Your name is ${name}, and you clicked ${count} times`);
});
return (
<div className="App">
<h1>Hello {name}</h1>
<form>
<label forHtml="name">My name is: </label>
<input
type="text"
placeholder="Bond... James Bond"
value={name}
onChange={event => setName(event.target.value)}
/>
</form>
<p>Click Counter: {count}</p>
<button onClick={() => setCount(count + 1)}>
Click
</button>
</div>
);
}
Dans cette application, nous avons deux fonctionnalités :
- L'utilisateur peut taper son nom dans le champ pour le voir dans le titre
- L'utilisateur peut cliquer pour incrémenter le compteur
Dans notre useEffect, pour chaque nouveau rendu, nous avons une fonction de rappel qui affichera un message dans la console pour récompenser l'utilisateur lorsqu'il cliquera sur le bouton.
🎤 Question time:
false|||true|||false
# Le `useEffect` de l'exemple ci-dessus contient un tableau de dépendances
[] vrai
[x] faux
# Le callback du `useEffect` s'exécutera lorsque le composant sera monté
[x] vrai
[] faux
# Le callback du `useEffect` s'exécutera lors de la mise à jour du composant
[x] vrai
[] faux

Notre useEffect ne contient pas du tout de tableau de dépendances (même un tableau vide).
Cela signifie que le callback s'exécutera pour chaque rendu.
Dans le stackblitz ci-dessus, tape quelque chose dans l'input. Tu devrais voir que le callback du useEffect (avec le message pour le clic) s'affiche dans la console chaque fois que tu tapes quelque chose.
Pas de tableau des dépendances = le callback du useEffect s'exécute pour chaque rendu (phase de mise à jour).
Maintenant, essaie d'ajouter un tableau vide comme deuxième argument, que remarque-tu ?
Maintenant, même lorsque l'utilisateur clique sur le bouton, le message ne s'affiche pas.
Mais attends... Nous voulons que le message s'affiche lors de la mise à jour du composant. Mais nous voulons que cela se produise uniquement lorsque la valeur du compteur change !
Pour cela, nous devons ajouter le compteur dans le tableau des dépendances. Essaie et voie ce qui se passe !
Tu devrais voir que le message s'affiche uniquement lorsque le compteur est mis à jour, pas lorsque le nom est mis à jour !
import React from "react";
export default function App() {
const [count, setCount] = React.useState(0);
const [name, setName] = React.useState("");
console.log('Component re-render');
React.useEffect(() => {
console.log(`Youhou ! You clicked ${count} times`);
}, [count]);
return (
<div className="App">
<h1>Hello {name}</h1>
<form>
<label forHtml="name">My name is: </label>
<input
type="text"
placeholder="Bond... James Bond"
value={name}
onChange={event => setName(event.target.value)}
/>
</form>
<p>Click Counter: {count}</p>
<button onClick={() => setCount(count + 1)}>
Click
</button>
</div>
);
}
Tableau des dépendances avec des variables = le callback du useEffect s'exécute chaque fois qu'une variable répertoriée voit sa valeur modifiée (phase de mise à jour).
Expérimenter
Essayons maintenant : utilise le bac à sable ci-dessus et crée un nouveau useEffect.
Celui-ci affichera un message dans la console à chaque fois que l'utilisateur tapera quelque chose 😝.
Voir la solution|||jsx|||Besoin d'aide ? |||0|||Masquer
import React from "react";
export default function App() {
const [count, setCount] = React.useState(0);
const [name, setName] = React.useState("");
console.log('Component re-render');
React.useEffect(() => {
console.log(`Youhou ! You clicked ${count} times`);
}, [count]);
React.useEffect(() => {
console.log(`Hey ! Your name is ${name}`);
}, [name]);
return (
<div className="App">
<h1>Hello {name}}</h1>
<form>
<label forhtml="name">My name is: </label>
<input
type="text"
placeholder="Bond... James Bond"
value={name}
onChange={event => setName(event.target.value)}
/>
</form>
<p>Click Counter: {count}</p>
<button onClick={() => setCount(count + 1)}>
Click
</button>
</div>
);
}
Regrouper useState et useEffect
Tu peux regrouper le useState avec le useEffect associé : cela rendra ton code plus lisible ;)
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log(`Youhou ! You clicked ${count} times`);
}, [count]);
const [name, setName] = React.useState("");
React.useEffect(() => {
console.log(`Hey ! Your name is ${name}`);
}, [name]);
💥 Phase de démontage
Voyons un dernier exemple concret sur la façon dont nous pouvons utiliser useEffect.
!--- App.js
import React from "react";
import TimerComponent from "./TimerComponent";
export default function App() {
const [isStart, setIsStart] = React.useState(false);
return (
<div className="App">
<h1>Welcome</h1>
{isStart && <TimerComponent />}
<button onClick={() => setIsStart((prevState) => !prevState)}>
{isStart ? `Stop Timer` : 'Start Timer'}
</button>
</div>
);
}
!--- TimerComponent.js
import React from "react";
function TimerComponent() {
const [timer, setTimer] = React.useState(0);
React.useEffect(() => {
console.log("timer started!");
const interval = setInterval(() => {
console.log("timer is incrementing");
setTimer((prev) => prev + 1);
}, 1000);
}, []);
return <h1>{timer}</h1>;
}
export default TimerComponent;
Dans cet exemple, nous avons un rendu conditionnel : le composant TimerComponent est affiché lorsque l'état isStart est vrai.
Le bouton inverse l'état isStart. Ainsi, lorsque tu cliques sur Start Timer, l'état isStart passe à true et le composant est affiché. Clique ensuite sur Stop Timer, l'état isStart passe à false et le composant est masqué ** MAIS ** , le message "timer is incrementing" continue à être affiché chaque seconde ?!?
Regardons maintenant TimerComponent.js...
À l'intérieur de TimerComponent, nous avons un useEffect avec un tableau des dépendances vide.
Ce qui signifie que le callback va être exécuté lorsque le composant sera monté sur le DOM. Étant donné que le composant apparaît et disparaît sur le DOM en fonction de l'état isStart, nous savons que la fonction de rappel du useEffect s'exécutera chaque fois que le composant réapparaîtra.
Dans ce useEffect, nous exécutons setInterval. setInterval va exécuter toutes les 1000 millisecondes la fonction de rappel fournie qui lui est fournie, ce qui signifie que chaque seconde ce code sera exécuté :
console.log("timer is incrementing");
setTimer((prevState) => prevState + 1);
Si tu regardes la console, tu peux voir que c'est exactement ce qui se passe : chaque seconde, la minuterie augmente de un, le composant sera alors re-rendu afin que nous puissions voir le nombre augmenter.
Note que setInterval exécute une fonction toutes les x millisecondes : c'est un fonctionnement indépendant de useEffect. Comme le tableau des dépendances est vide, useEffect ne s'exécutera qu'une seule fois, lorsque le composant est monté sur le DOM. Seule la fonction de rappel de setInterval est réexécutée : le useEffect sert à lancer le timer.
En fait, setInterval est une fonctionnalité qui vient de l'API de ton navigateur. Ce qui signifie que ton navigateur enregistrera le callback et l'exécutera toutes les secondes.
Ok, reste avec moi une seconde... Il y a un problème avec le code suivant... Regarde la console.
Lorsque tu arrêtes le timer, que vois-tu dans la console ?
Le console.log continue de s'exécuter, même si le composant n'est plus là
Pourquoi cela arrive-t-il?
Comme dit précédemment, le navigateur continuera à déclencher le callback du timer toutes les secondes, que le composant soit monté ou non.
Pour arrêter l'intervalle, nous devons utiliser la fonction clearInterval.
Mais quand voulons-nous exécuter cette fonction ?
Nous voulons l'exécuter juste avant que le composant ne soit démonté du DOM, n'est-ce pas ? Donc avant que notre composant ne disparaisse, on arrête l'intervalle et voilà.
Comment faire ça?
En retournant une fonction du callback dans notre ̀useEffect.
Essayons : prends le code précédent et, dans le callback du useEffect, renvoie une fonction qui arrête le timer. Nous pouvons arrêter un timer en utilisant la fonction clearInterval(intervalName).
return () => clearInterval(interval);
En live (regarde TimerComponent) :
!--- App.js
import React from "react";
import TimerComponent from "./TimerComponent";
export default function App() {
const [isStart, setIsStart] = React.useState(false);
return (
<div className="App">
<h1>Welcome</h1>
{isStart && <TimerComponent />}
<button onClick={() => setIsStart((prevState) => !prevState)}>
{isStart ? `Stop Timer` : 'Start Timer'}
</button>
</div>
);
}
!--- TimerComponent.js
import React from "react";
function TimerComponent() {
const [timer, setTimer] = React.useState(0);
React.useEffect(() => {
console.log("timer started!");
const interval = setInterval(() => {
console.log("timer is incrementing");
setTimer((prev) => prev + 1);
}, 1000);
return () => clearInterval(interval)
}, []);
return <h1>{timer}</h1>;
}
export default TimerComponent;
La fonction de rappel que tu as fournie au return du callback du useEffect sera exécutée avant le démontage du composant.
Note également que le retour est également exécuté à chaque mise à jour si le tableau des dépendances est rempli (ce qui n'est pas notre cas ici).
Quiz
true|||true|||false
# Si je ne fournis pas le tableau des dépendances à `useEffect`, que se passera-t-il ?
[] Il n'exécutera jamais le callback
[x] Le callback s'exécutera pour chaque rendu
[] Il lancera une erreur
# Que se passe-t-il si je retourne une fonction dans le callback d'un `useEffect` ?
[] Elle sera exécutée à chaque nouveau rendu
[] Elle ne sera jamais exécutée
[x] Elle sera exécutée lorsque le composant sera démonté
[] Elle sera exécutée lorsque le composant sera monté
# Si j'ajoute un élément au tableau des dépendances (plusieurs réponses sont possibles)
[x] Le callback `useEffect` s'exécutera à chaque mise à jour de cet élément
[x] Le callback `useEffect` s'exécutera lors du montage du composant
[] Le callback `useEffect` s'exécutera lorsque le composant se démontera
[] Le callback `useEffect` ne s'exécutera jamais
✅ En résumé
useEffectest le hook que tu peux utiliser pour accéder aux phases du cycle de vie de React.useEffectprend deux arguments :- Une fonction de rappel (callback),
- Un tableau des dépendances.
- Le callback du
useEffectsera appelé- Quand le composant sera monté (phase de montage)
- Après le rendu lors d'une phase de mise à jour ⇒ Si une des valeurs du tableau des dépendances a changé, ou s'il n'y avait pas de dépendance (phase de mise à jour)
- Avant le démontage du composant si tu as retourné une fonction à la fin du callback (phase de démontage)
Peut-être que durant ta vie de dev, tu utiliseras des composants classe. Jette un œil à l'excellent Thinking in React Hooks : cela t'aidera à voir des parallèles entre useEffect et les méthodes componentDidMount, componentDidUpdate et componentDidUnmount.
💪 Challenge
Tu trouveras dans ce stackblitz le point de départ de ce challenge.
Pour ce challenge, tu vas pratiquer useEffect. Le template contient un formulaire avec un input et deux boutons.
- Fork le stackblitz
- Ajoute deux états (en utilisant useState)
- un état appelé
emailde type string, pour stocker la valeur de l'input - Un état booléen appelé
isUserLoggedIndéfini surfalsepar défaut
- un état appelé
- Lorsque l'input change, l'état de l'email doit également changer
- Ajoute un
useEffectpour que, lorsque l'état de l'email change, un message s'affiche sur la console (""email has changed") (Astuce : tu dois utiliseruseEffect` avec l'email dans le tableau des dépendances) - Lorsque le formulaire est soumis, modifie l'état de
isUserLoggedInpour qu'il soit vrai. Lorsque l'utilisateur clique surDéconnexion, change l'étatisUserLoggedInsur false. - Lorsque isUserLoggedIn est vrai, affiche le composant
<Message />(Astuce : utilise l'opérateur ternaire) - Ajoute un
useEffectpour que, lorsque le composant<Message />est affiché, un message soit affiché dans la console ("user is logged in") (Astuce : tu dois utiliser *useEffect dans le composant<Message />) - Ajoute un
useEffectdans le composant<Message />afin que, lorsque le composant se démonte, un message s'affiche sur la console disant 'Bye !' (Astuce : renvoie une fonction dans le callback duuseEffect)
🧐 Critères de validation
- [ ] Lorsque l'email change,
"email has changed"s'affiche dans la console - [ ] Lorsque l'utilisateur se connecte,
"user is logged in"s'affiche dans la console - [ ] Lorsque l'utilisateur se déconnecte,
"Bye !"s'affiche dans la console
Ressources partagées sur cette quête
- Une autre vision du hook UseEffect (en français).https://www.youtube.com/watch?v=OC9swIBpD_U&list=PLjwdMgw5TTLWom67YfZuha-1iYzIirwJR&index=16
- Pareil, très bon exemple pour comprendre le useEffecthttps://essential-dev-skills.com/reactjs/les-hooks/use-effect
- explication très simple et courte sur unmounthttps://dev.to/robmarshall/how-to-use-componentwillunmount-with-functional-components-in-react-2a5g
- Explication du useEffect en Français, particulièrement util pour le point 8 de la quête, l'exemple niveau 2 clean uphttps://react-facile.fr/cours/comment-bien-utiliser-le-hook-useeffect/
- Vidéo de 4mins qui permet de comprendre les dépendances useEffect de manière simplifiéehttps://www.youtube.com/watch?v=jQc_bTFZ5_I&ab_channel=TheNetNinja
- Parfait pour l'explication de UseEffect et surtout pour la compréhension du return pour le démontage https://dmitripavlutin.com/react-useeffect-explanation/
- Très bonne explications (GrafikArt bien sur)https://www.youtube.com/watch?v=OC9swIBpD_U