
Annulation des requêtes (AbortController)
Apprenez à annuler les requêtes API (fetch) dans vos composants React en utilisant AbortController et la fonction de nettoyage de useEffect pour éviter les erreurs.
Pourquoi annuler les requêtes ? Le problème des mises à jour sur composants démontés
Dans notre approche précédente utilisant `useEffect` pour lancer des appels API au montage, nous avons géré les états de chargement, succès et erreur. Cependant, un problème subtil mais important subsiste : que se passe-t-il si le composant est démonté (unmounted) avant que la requête API ne soit terminée ?
Imaginez un utilisateur naviguant rapidement entre différentes pages. Un composant `UserProfile` monte, lance une requête `fetch` pour obtenir les données de l'utilisateur, mais l'utilisateur navigue ailleurs avant que la réponse n'arrive. La promesse `fetch` finira par se résoudre (en succès ou en erreur), et le code dans les blocs `.then()` ou `.catch()` tentera d'appeler `setData`, `setError` ou `setLoading`.
Or, le composant n'existe plus dans l'arbre React à ce moment-là. Tenter de mettre à jour l'état d'un composant démonté est une opération invalide. React nous avertit généralement de ce problème avec un message dans la console du type : "Warning: Can't perform a React state update on an unmounted component." Bien que souvent juste un avertissement, cela indique une fuite potentielle (la promesse et ses callbacks restent en mémoire) et une logique incorrecte. Dans des cas plus complexes, cela pourrait même mener à des erreurs.
Pour résoudre ce problème proprement, nous devons avoir un moyen d'annuler la requête réseau si le composant est démonté avant qu'elle ne se termine.
`AbortController` : La solution standard pour annuler `fetch`
L'API standard des navigateurs pour gérer l'annulation des requêtes (et d'autres opérations asynchrones) est l'AbortController. Elle fonctionne en tandem avec l'API `fetch`.
Le principe est le suivant :
- Vous créez une instance d'
AbortController:const controller = new AbortController(); - Vous récupérez son objet `signal` :
const signal = controller.signal; - Vous passez ce `signal` en option à votre appel `fetch` :
fetch(url, { signal }); - Si vous souhaitez annuler la requête, vous appelez la méthode
controller.abort(). - Lorsque
abort()est appelée, la promesse retournée par `fetch` est immédiatement rejetée avec une erreur spéciale dont lenameest'AbortError'.
Comment intégrer cela dans notre `useEffect` ? La fonction de nettoyage retournée par `useEffect` est l'endroit idéal pour appeler controller.abort(). Cette fonction de nettoyage s'exécute lorsque le composant est sur le point d'être démonté, ou juste avant que l'effet ne se ré-exécute (si ses dépendances changent).
Implémentation dans `useEffect` avec nettoyage
Modifions notre exemple précédent pour inclure `AbortController` :
import React, { useState, useEffect } from 'react';
function ListeAvecAnnulation() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// 1. Créer l'AbortController à l'intérieur de l'effet
const controller = new AbortController();
const signal = controller.signal;
const fetchData = async () => {
setLoading(true);
setError(null);
// setData([]); // Optionnel
try {
// 2. Passer le signal à fetch
const response = await fetch('https://api.example.com/items', { signal });
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
// 4. Vérifier si l'erreur est une AbortError
if (err.name === 'AbortError') {
console.log('Requête annulée par le composant');
// Ne pas définir d'état d'erreur dans ce cas, c'est intentionnel
} else {
// C'est une autre erreur (réseau, serveur, etc.)
setError(err.message);
console.error("Erreur fetch:", err);
}
} finally {
// S'assurer que setLoading est mis à false uniquement si la requête n'a pas été annulée
// (ou si l'on veut arrêter le loading même en cas d'annulation)
// Solution simple: le mettre à false ici quoi qu'il arrive après un try/catch gérant AbortError
setLoading(false);
}
};
fetchData();
// 3. La fonction de nettoyage appelle controller.abort()
return () => {
controller.abort();
};
}, []); // Ne pas oublier les dépendances si l'URL ou d'autres paramètres changent
// ... Rendu conditionnel (idem section précédente) ...
if (loading) return Chargement...;
if (error) return Erreur : {error};
return (
Liste (avec annulation)
{data.map(item => (- {item.name}
))}
);
}
export default ListeAvecAnnulation;Points clés de l'implémentation :
- L'
AbortControllerest créé à l'intérieur de `useEffect`. - Son `signal` est passé à l'appel `fetch`.
- La fonction de nettoyage retournée par `useEffect` appelle `controller.abort()`.
- Dans le bloc `catch`, nous vérifions spécifiquement si `err.name === 'AbortError'`. Si c'est le cas, nous savons que l'annulation était intentionnelle (due au démontage ou à un changement de dépendance) et nous ne traitons pas cela comme une erreur applicative à afficher à l'utilisateur.
Considérations pour `axios` et conclusion
Si vous utilisez axios, le mécanisme d'annulation est légèrement différent, bien que les versions récentes d'axios supportent également `AbortController` de manière similaire à `fetch`. Historiquement, axios utilisait un `CancelToken`. Avec `AbortController` (recommandé maintenant), vous pouvez faire :
// Dans useEffect
const controller = new AbortController();
axios.get('/api/items', { signal: controller.signal })
.then(response => { /* ... */ })
.catch(error => {
if (axios.isCancel(error)) { // Vérification spécifique à axios pour l'annulation
console.log('Requête Axios annulée:', error.message);
} else {
// Gérer les autres erreurs
}
});
// Cleanup
return () => controller.abort();Bien que l'implémentation manuelle de l'annulation avec `AbortController` ajoute un peu de code (boilerplate), elle est essentielle pour écrire des composants React robustes qui interagissent avec des API asynchrones. Elle prévient les erreurs liées aux mises à jour d'état sur des composants démontés.
Heureusement, comme nous le verrons bientôt, les bibliothèques de data fetching modernes comme React Query ou SWR gèrent souvent l'annulation des requêtes pour vous en coulisses, simplifiant grandement ce processus et vous permettant de vous concentrer davantage sur la logique métier.