useEffect est un hook React qui permet d'exécuter des effets secondaires dans les composants fonctionnels, remplaçant les méthodes du cycle de vie des composants de classe : componentDidMount, componentDidUpdate et componentWillUnmount. Selon React Documentation (2025), useEffect s'exécute après que React a validé les modifications dans le DOM, garantissant l'accès à l'arborescence DOM réelle. Le hook accepte une fonction d'effet et un tableau optionnel de dépendances qui contrôle la fréquence d'exécution.
Points clés à retenir
useEffect est un hook ajouté dans React 16.8 pour réaliser des effets secondaires dans les composants fonctionnels. Les effets secondaires sont des opérations qui ne sont pas directement liées au rendu de l'interface utilisateur : requêtes HTTP vers des API, abonnements à des événements, travail avec des minuteries, manipulations du DOM, journalisation et intégration avec des bibliothèques tierces.
Avant les hooks, toutes ces opérations devaient être placées dans les méthodes du cycle de vie des composants de classe : componentDidMount pour l'initialisation, componentDidUpdate pour réagir aux changements de props, componentWillUnmount pour le nettoyage. useEffect a unifié les trois scénarios en une seule API, où le tableau de dépendances détermine quand l'effet doit s'exécuter. Cela a simplifié la logique et réduit la duplication de code, en particulier dans les scénarios d'abonnement.
Selon React DevTools Usage Survey (2024), useEffect est le deuxième hook le plus populaire après useState, utilisé dans 89% des applications React. La plupart des développeurs l'utilisent pour récupérer des données, synchroniser avec des systèmes externes et gérer les abonnements aux événements DOM.
import { useEffect } from 'react';
function UserProfile({ userId }) {
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
}
useEffect exécute la fonction d'effet passée après que React a terminé le rendu et mis à jour le DOM. C'est une différence clé par rapport aux calculs au moment du rendu : l'effet ne bloque pas le rendu, ce qui est critique pour la performance UX. Si les effets s'exécutaient de manière synchrone, les utilisateurs verraient une interface figée pendant le chargement des données.
Le cycle de vie d'un effet typique se compose de trois phases. Au montage du composant, React exécute l'effet. À chaque mise à jour, si au moins une dépendance du tableau a changé, React exécute d'abord la fonction de nettoyage de l'effet précédent, puis le nouvel effet. Au démontage du composant, seule la fonction de nettoyage est exécutée.
Selon React Team — useEffect RFC (2024), l'implémentation interne de useEffect utilise une file d'attente d'effets secondaires dans l'arbre fiber. Après avoir validé les modifications (commit phase), React parcourt cette file d'attente et appelle les fonctions d'effet dans l'ordre où elles ont été déclarées dans le composant. Chaque nœud fiber stocke une référence à l'effet précédent pour un nettoyage et un redémarrage corrects.
| Étape | Action de React | Quand elle s'exécute |
|---|---|---|
| Montage | Appeler la fonction effect | Après le premier rendu |
| Mise à jour | nettoyage → effect | Quand les dépendances changent |
| Démontage | Nettoyage uniquement | Quand le composant est supprimé |
Le tableau de dépendances — le deuxième argument de useEffect — détermine quand l'effet doit être redémarré. React compare chaque valeur du tableau avec le rendu précédent en utilisant Object.is. Si au moins une valeur a changé, l'effet s'exécute à nouveau. Si le tableau est vide ([]), l'effet s'exécute une seule fois après le montage.
Choisir les bonnes dépendances est la partie la plus difficile du travail avec useEffect. Le tableau doit inclure toutes les variables et fonctions utilisées dans l'effet qui peuvent changer entre les rendus. Omettre une dépendance entraîne des fermetures obsolètes (stale closures) — l'effet voit une valeur périmée du rendu précédent. Inclure des dépendances inutiles provoque des redémarrages excessifs et des bogues potentiels.
// Les dépendances contrôlent quand l'effet se réexécute
useEffect(() => {
document.title = `User: ${user.name}`;
}, [user.name]); // réexécuter uniquement lorsque user.name change
// eslint-disable-next-line react-hooks/exhaustive-deps
// Si vous omettez une dépendance, vous obtenez des données obsolètes
React fournit eslint-plugin-react-hooks avec la règle exhaustive-deps, qui vérifie automatiquement l'exhaustivité du tableau de dépendances. Selon Meta Engineering Blog (2024), l'activation de ce plugin réduit les bogues liés aux hooks de 72%. Il est recommandé de corriger tous les avertissements exhaustive-deps plutôt que de les supprimer avec un commentaire, sauf dans les cas rares avec une logique personnalisée.
Si vous ne transmettez pas du tout le tableau de dépendances, useEffect s'exécutera après chaque rendu. Cela peut être utile pour la synchronisation du DOM ou la journalisation, mais c'est le plus souvent une erreur : l'effet s'exécute trop fréquemment, ce qui entraîne une perte de performance. Dans la plupart des cas, vous devez passer un tableau vide (une fois au montage) ou un tableau avec des props/state spécifiques.
Un tableau vide ([]) signifie que l'effet ne dépend d'aucune valeur et s'exécute strictement une fois. Cela équivaut à componentDidMount dans les composants de classe. Cependant, il faut se rappeler : si l'effet utilise des props ou un état qui ne sont pas listés dans le tableau de dépendances, l'effet utilisera leurs valeurs initiales et ne verra jamais les mises à jour. C'est ce qu'on appelle la stale capture et c'est souvent une source de bogues difficiles à trouver.
| Tableau de dépendances | Comportement | Équivalent en classes |
|---|---|---|
| Sans argument | Après chaque rendu | componentDidUpdate |
| [] | Une fois au montage | componentDidMount |
| [a, b] | Quand a ou b change | Analogue de ComponentWillReceiveProps |
| return cleanup | Gérer le démontage | componentWillUnmount |
La fonction de nettoyage est une fonction que useEffect peut retourner depuis son callback. React l'appelle au démontage du composant et avant de réexécuter l'effet lorsque les dépendances changent. Le nettoyage est nécessaire pour annuler les abonnements, les minuteries, les requêtes et toutes les ressources qui doivent être libérées.
Un exemple typique est un abonnement WebSocket. Au montage, une connexion est créée ; à la mise à jour des dépendances, elle est recréée (le nettoyage ferme l'ancienne, l'effet ouvre une nouvelle) ; au démontage, elle est fermée. Sans nettoyage, chaque remontage du composant créerait une nouvelle connexion WebSocket, entraînant des fuites de mémoire et de multiples connexions.
useEffect(() => {
const socket = new WebSocket('wss://api.example.com');
socket.onmessage = event => setData(event.data);
// Fonction de nettoyage — s'exécute au démontage et avant la réexécution
return () => {
socket.close();
};
}, []);
Selon React Documentation (2025), AbortController est l'approche moderne pour annuler les requêtes fetch dans le nettoyage. Si l'effet fait une requête HTTP et que le composant est démonté avant son achèvement, la requête continue de s'exécuter et setState après le démontage provoque une erreur. Créez un AbortController dans l'effet et appelez controller.abort() dans le nettoyage pour annuler la requête.
L'erreur la plus courante est l'omission de dépendances. Par exemple, l'effet utilise la prop userId, mais le tableau de dépendances est vide. En conséquence, l'effet s'exécute une fois avec la valeur initiale de userId et ne réagit jamais à ses changements. Le développeur voit que le composant reçoit un nouveau userId, mais les données ne sont pas mises à jour. eslint-plugin-react-hooks avec la règle exhaustive-deps détecte ces bogues automatiquement.
// ❌ Race condition — pas d'annulation
useEffect(() => {
fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);
// ✅ Corrigé avec AbortController
useEffect(() => {
const controller = new AbortController();
fetch(`/api/user/${userId}`, { signal: controller.signal })
.then(res => setUser(res));
return () => controller.abort();
}, [userId]);
Pour résoudre le problème de la boucle infinie, évitez de placer dans useEffect une logique qui met à jour l'état en fonction de l'état précédent. Utilisez la forme fonctionnelle de setState ou déplacez les calculs en dehors de l'effet. Si l'effet s'abonne à des événements de stockage ou de navigateur, assurez-vous que l'instance de l'écouteur est créée une fois, pas à chaque rendu.
Foire aux questions
Directement — non, car useEffect s'attend à ce qu'une fonction synchrone ou undefined soit retournée. Si le callback est déclaré comme async, il retourne une Promise que React ignore, et le mécanisme de nettoyage cesse de fonctionner. Solution : appelez une fonction async dans l'effet : useEffect(() => { async function load() { ... }; load(); }, []).
Il n'y a pas de limite. React recommande de séparer les logiques non liées dans des useEffect individuels, même s'ils ont le même tableau de dépendances. Chaque effet doit être responsable d'une tâche secondaire clairement définie : un pour les abonnements, un autre pour le chargement des données, un troisième pour synchroniser le titre de l'onglet. Cela simplifie la compréhension et le débogage.
Dans React Strict Mode (mode développement uniquement), tous les effets sont montés, démontés et remontés. C'est une fonctionnalité, pas un bogue — React vérifie que le nettoyage fonctionne correctement. Si après démontage et remontage l'effet se comporte mal (par exemple, abonnements en double), votre nettoyage est incomplet. En production, l'effet s'exécute une fois.
Utilisez AbortController. Créez un controller dans l'effet, passez controller.signal à fetch et appelez controller.abort() dans le nettoyage. Si le composant est démonté avant la fin de la requête, fetch est annulé et setState ne sera pas appelé. Cela évite les conditions de course et l'erreur “Can't perform a React state update on an unmounted component”.
useEffect s'exécutera après chaque rendu sans exception. Cela signifie que tout setState dans l'effet provoquera un nouveau rendu → nouvel effet → boucle infinie. En pratique, un effet sans tableau de dépendances est presque toujours une erreur. Les exceptions sont la journalisation ou la synchronisation avec un système externe où chaque rendu nécessite une synchronisation.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi