
Gestion des requêtes POST, PUT, DELETE
Apprenez à gérer les requêtes POST, PUT, DELETE en React pour modifier les données serveur, et comment synchroniser l'UI avec fetch/axios ou React Query (useMutation).
Au-delà de la lecture : Modifier les données serveur
Jusqu'à présent, notre exploration du data fetching s'est principalement concentrée sur la récupération de données existantes depuis une API, typiquement via des requêtes HTTP `GET`. Cependant, les applications web interactives nécessitent également de pouvoir modifier ces données côté serveur. Ces opérations de modification sont appelées des mutations.
Les méthodes HTTP standard pour effectuer ces mutations sont :
- `POST` : Généralement utilisé pour créer une nouvelle ressource (ex: créer un nouvel utilisateur, ajouter un nouvel article de blog, poster un commentaire).
- `PUT` : Utilisé pour remplacer complètement une ressource existante par une nouvelle version (ex: mettre à jour l'intégralité du profil utilisateur).
- `PATCH` : Utilisé pour appliquer une mise à jour partielle à une ressource existante (ex: changer uniquement l'adresse email d'un utilisateur). `PATCH` est souvent préféré à `PUT` pour les mises à jour car il est moins verbeux.
- `DELETE` : Utilisé pour supprimer une ressource existante (ex: supprimer un article, désactiver un compte).
Contrairement aux requêtes `GET` qui sont souvent déclenchées au chargement d'un composant pour afficher des données, les mutations sont typiquement initiées par une action de l'utilisateur : la soumission d'un formulaire, le clic sur un bouton "Enregistrer", "Supprimer", etc.
Effectuer des mutations avec `fetch` ou `axios`
Réaliser une requête de mutation avec `fetch` ou `axios` est similaire à une requête `GET`, mais nécessite de spécifier la méthode HTTP appropriée et, pour `POST`, `PUT`, ou `PATCH`, d'envoyer les données à modifier dans le corps (body) de la requête, souvent au format JSON.
Voici un exemple simple de création d'un nouvel item via un formulaire en utilisant `fetch` dans un gestionnaire d'événement :
import React, { useState } from 'react';
function FormulaireAjoutItem() {
const [itemName, setItemName] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState(null);
const handleSubmit = async (event) => {
event.preventDefault();
setIsSubmitting(true);
setError(null);
try {
const response = await fetch('/api/items', { // URL de l'endpoint POST
method: 'POST',
headers: {
'Content-Type': 'application/json', // Indiquer qu'on envoie du JSON
// Ajouter d'autres headers si nécessaire (ex: Authorization)
},
body: JSON.stringify({ name: itemName }), // Les données à envoyer
});
if (!response.ok) {
// Gérer les erreurs serveur
const errorData = await response.json().catch(() => ({})); // Essayer de lire le corps de l'erreur
throw new Error(`Erreur ${response.status}: ${errorData.message || 'Impossible de créer l'item'}`);
}
// Succès !
console.log('Item créé avec succès !');
setItemName(''); // Vider le formulaire
// !!! PROBLEME: Comment mettre à jour la liste des items affichée ailleurs ?
} catch (err) {
setError(err.message);
console.error("Erreur lors de la création:", err);
} finally {
setIsSubmitting(false);
}
};
return (
);
}
export default FormulaireAjoutItem;L'utilisation d'`axios` serait similaire, avec `axios.post(url, data, config)`, `axios.put(...)`, `axios.delete(...)`. `axios` gère la sérialisation JSON du corps et la gestion des erreurs HTTP de manière un peu plus directe.
Le défi : Synchroniser l'état client après une mutation
Le principal défi après une mutation réussie (POST, PUT, DELETE) est de s'assurer que l'état de l'interface utilisateur (l'état client) reflète correctement les changements effectués sur le serveur. Si nous avons une liste d'items affichée dans un autre composant (probablement chargée via une requête `GET`), cette liste est maintenant obsolète après l'ajout ou la suppression d'un item.
Avec l'approche manuelle (`fetch`/`axios` dans les handlers), nous avons plusieurs options, mais aucune n'est idéale :
- Rafraîchir toute la page : Simple mais annule les bénéfices d'une SPA.
- Déclencher manuellement un refetch de la liste : Il faudrait passer une fonction de refetch au composant de formulaire, ce qui crée un couplage et peut devenir complexe.
- Mettre à jour manuellement l'état client : Si l'état de la liste est géré globalement (Context, Redux, Zustand), on pourrait essayer de mettre à jour cet état localement après la mutation. C'est rapide pour l'UX mais peut être très complexe à maintenir et source d'erreurs (désynchronisation si l'API retourne des données différentes, gestion des échecs, etc.).
C'est précisément pour résoudre ce problème de synchronisation de manière élégante et robuste que les bibliothèques de data fetching dédiées offrent des solutions spécifiques pour les mutations.
Solution élégante : `useMutation` de React Query (TanStack Query)
React Query (TanStack Query) propose le hook `useMutation` spécialement conçu pour gérer les opérations de mutation.
const { mutate, mutateAsync, status, isLoading, isError, error, data, ... } = useMutation({ mutationFn, options });- `mutationFn` (obligatoire) : Une fonction asynchrone qui prend les variables nécessaires à la mutation et effectue l'appel API (POST, PUT, DELETE). Elle doit retourner une promesse qui se résout avec le résultat ou lève une erreur.
- `options` (optionnel) : Un objet pour configurer les callbacks de cycle de vie et le comportement :
- `onSuccess(data, variables, context)` : Appelé après une mutation réussie. C'est l'endroit idéal pour invalider les requêtes (`queryClient.invalidateQueries`) qui dépendent des données modifiées, forçant React Query à les refetcher automatiquement.
- `onError(error, variables, context)` : Appelé si la mutation échoue.
- `onSettled(data, error, variables, context)` : Appelé après succès ou échec.
- `onMutate(variables)` : Callback pour les mises à jour optimistes (avancé).
Le hook retourne :
- `mutate` : La fonction à appeler pour déclencher la mutation. Elle prend en argument les variables à passer à `mutationFn`.
- `mutateAsync` : Une version de `mutate` qui retourne une promesse.
- Divers états (`isLoading`, `isError`, `status`, `error`, `data` du dernier succès) pour suivre l'état de la mutation elle-même.
Exemple d'utilisation pour l'ajout d'item :
import React, { useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
const addItemAPI = async (newItem) => {
const { data } = await axios.post('/api/items', newItem);
return data;
};
function FormulaireAjoutItemRQ() {
const [itemName, setItemName] = useState('');
const queryClient = useQueryClient(); // Accès au client pour invalider
const { mutate, isLoading, isError, error } = useMutation({
mutationFn: addItemAPI,
onSuccess: () => {
// Succès ! Invalider la query 'items' pour forcer son rechargement
queryClient.invalidateQueries({ queryKey: ['items'] });
console.log('Item créé ! Cache invalidé.');
setItemName(''); // Vider le formulaire
},
onError: (err) => {
console.error("Erreur mutation:", err);
}
});
const handleSubmit = (event) => {
event.preventDefault();
mutate({ name: itemName }); // Déclencher la mutation avec les variables
};
return (
);
}
export default FormulaireAjoutItemRQ;Avec `queryClient.invalidateQueries(['items'])`, React Query s'occupe intelligemment de refetcher les données de la liste d'items partout où elle est utilisée (via `useQuery` avec la clé `['items']`), assurant la synchronisation de manière découplée et efficace.
Gestion des mutations avec SWR
SWR n'a pas de hook dédié comme `useMutation`. La gestion des mutations se fait généralement en effectuant l'appel API manuellement (avec `fetch` ou `axios` dans un handler), puis en utilisant la fonction `mutate` (soit la fonction globale importée de `swr`, soit celle retournée par `useSWR`) pour informer SWR que les données mises en cache doivent être mises à jour.
Il y a deux façons principales d'utiliser `mutate` après une mutation :
- Déclencher une revalidation : `mutate(key)` (où `key` est la clé des données à refetcher, ex: l'URL `/api/items`). SWR va alors relancer le `fetcher` associé à cette clé pour obtenir les données à jour. C'est similaire à l'invalidation de React Query.
- Mise à jour optimiste : `mutate(key, newData, options)` permet de mettre à jour immédiatement le cache SWR avec `newData` (avant même la confirmation du serveur) pour une meilleure réactivité perçue, puis éventuellement de déclencher une revalidation ensuite.
import React, { useState } from 'react';
import { useSWRConfig } from 'swr'; // Hook pour accéder à la fonction mutate globale
import axios from 'axios';
function FormulaireAjoutItemSWR() {
const [itemName, setItemName] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState(null);
const { mutate } = useSWRConfig(); // Obtenir la fonction mutate
const handleSubmit = async (event) => {
event.preventDefault();
setIsSubmitting(true);
setError(null);
try {
await axios.post('/api/items', { name: itemName });
// Succès ! Déclencher la revalidation pour la clé '/api/items'
mutate('/api/items');
console.log('Item créé ! Revalidation SWR déclenchée.');
setItemName('');
} catch (err) {
setError(err.message);
} finally {
setIsSubmitting(false);
}
};
// ... reste du formulaire (similaire à l'exemple fetch) ...
return (
);
}
export default FormulaireAjoutItemSWR;Bien que fonctionnelle, l'approche de SWR pour les mutations est souvent perçue comme moins structurée que celle de React Query avec `useMutation`, qui encapsule l'état de la mutation elle-même et ses callbacks de manière plus intégrée.
En conclusion, gérer les requêtes POST, PUT, DELETE est essentiel. Si l'approche manuelle avec `fetch`/`axios` est possible, elle soulève le défi de la synchronisation de l'état client. Des bibliothèques comme React Query (avec `useMutation`) ou SWR (avec `mutate`) offrent des solutions beaucoup plus robustes et élégantes pour gérer ces mutations et maintenir la cohérence entre le serveur et le client.