Impossible de mettre à jour l'état React sur un composant démonté : comment corriger et accélérer le code

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Un bug familier qui énerve chaque développeur React

\n

Avez-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.

\n

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\n

Pourquoi React crie-t-il à propos d'un composant démonté ?

\n

Imaginez : 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 ! ».

\n

En TypeScript, ce problème est particulièrement visible si vous utilisez un typage strict et oubliez les fonctions de nettoyage (cleanup) dans useEffect.

\n\n

Exemple classique du problème (TypeScript)

\n
import 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};
\n

Si 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.

\n\n

3 méthodes pour corriger l'erreur (avec des exemples en TypeScript)

\n

Examinons trois approches fiables qui résolvent le problème et améliorent les performances de l'application React.

\n\n

Méthode 1 : Drapeau avec useRef (la plus populaire)

\n

Créons un drapeau ref qui indique si le composant est monté. On met à jour l'état uniquement si le drapeau est true.

\n
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};
\n

Avantages : simplicité, ne nécessite pas de bibliothèques supplémentaires. Inconvénient : il ne faut pas oublier le cleanup.

\n\n

Méthode 2 : AbortController pour annuler la requête fetch (recommandée)

\n

Une 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.

\n
import 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                

Blogs

Book Recommendations