Un bug familier qui énerve chaque développeur React
\nAvez-vous déjà vu dans la console du navigateur l'avertissement Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. ? Si oui — vous n'êtes pas seul. Cette question sur Stack Overflow a rassemblé des milliers de vues et est devenue un véritable casse-tête pour les débutants et les développeurs React expérimentés.
Dans cet article, nous allons comprendre pourquoi cette erreur se produit, comment la corriger avec TypeScript, et surtout comment optimiser les performances de votre application pour que ces avertissements n'apparaissent plus. C'est parti !
\n\nPourquoi React crie-t-il à propos d'un composant démonté ?
\nImaginez : l'utilisateur a ouvert une page, a commencé à charger des données (par exemple via fetch ou axios), mais n'a pas attendu la réponse et est passé à un autre écran. Le composant qui a initié la requête est déjà démonté (supprimé du DOM), mais l'opération asynchrone est toujours en cours. Lorsque la réponse arrive, le code tente de mettre à jour l'état (state) d'un composant qui n'existe plus. React s'indigne à juste titre : « Hé, je ne peux pas mettre à jour ce qui n'existe pas ! ».
En TypeScript, ce problème est particulièrement visible si vous utilisez un typage strict et oubliez les fonctions de nettoyage (cleanup) dans useEffect.
Exemple classique du problème (TypeScript)
\nimport React, { useState, useEffect } from 'react';\n\ninterface User {\n id: number;\n name: string;\n}\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n const [user, setUser] = useState<User | null>(null);\n\n useEffect(() => {\n fetch(`/api/users/${userId}`)\n .then(res => res.json())\n .then(data => setUser(data)); // Erreur ! Le composant peut être démonté\n }, [userId]);\n\n return <div>{user?.name}</div>;\n};\nSi l'utilisateur change rapidement de route, le composant UserProfile sera supprimé et setUser sera appelé pour rien. On obtient une fuite mémoire et une erreur dans la console.
3 méthodes pour corriger l'erreur (avec des exemples en TypeScript)
\nExaminons trois approches fiables qui résolvent le problème et améliorent les performances de l'application React.
\n\nMéthode 1 : Drapeau avec useRef (la plus populaire)
\nCréons un drapeau ref qui indique si le composant est monté. On met à jour l'état uniquement si le drapeau est true.
import React, { useState, useEffect, useRef } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n const [user, setUser] = useState<User | null>(null);\n const isMounted = useRef(true);\n\n useEffect(() => {\n // Réinitialiser le drapeau à chaque montage\n isMounted.current = true;\n\n fetch(`/api/users/${userId}`)\n .then(res => res.json())\n .then(data => {\n if (isMounted.current) {\n setUser(data);\n }\n });\n\n // Cleanup : mettre le drapeau à false lors du démontage\n return () => {\n isMounted.current = false;\n };\n }, [userId]);\n\n return <div>{user?.name}</div>;\n};\nAvantages : simplicité, ne nécessite pas de bibliothèques supplémentaires. Inconvénient : il ne faut pas oublier le cleanup.
\n\nMéthode 2 : AbortController pour annuler la requête fetch (recommandée)
\nUne approche moderne et correcte consiste à annuler la requête elle-même, pas seulement ignorer la réponse. Cela économise les ressources du serveur et du client.
\nimport React, { useState, useEffect } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n const controller = new AbortController();\n const signal = con