Comment effectuer une mutation côté serveur

Apprends à modifier des données côté serveur avec React Router 7 et la méthode action : ajout, édition, suppression d’utilisateurs, et protection des données sensibles.

4 min read

Débloquez cette leçon

Entrez votre email pour accéder gratuitement à ce contenu.

Dans cette leçon, on va apprendre à modifier des données — ajouter, éditer ou supprimer un utilisateur — côté serveur avec React Router 7. On verra comment :

  • Protéger les données confidentielles et éviter de les exposer côté client
  • Déclarer une action pour recevoir et traiter un formulaire en POST
  • Comprendre le cycle loader ⇒ action ⇒ loader pour garder l’UI synchronisée

Qu’est-ce qu’une mutation ?

Une mutation est une opération qui modifie l’état de ton application ou de ta base de données. Par opposition au loader (qui ne lit que des données), l’action va :

  1. Créer un nouvel enregistrement
  2. Modifier un enregistrement existant
  3. Supprimer un enregistrement

Comme on touche à la source de vérité (la DB), ce traitement doit absolument se faire côté serveur pour :

  • Protéger les données confidentielles
  • Appliquer des règles d’autorisation (ex : seul l’admin peut supprimer un utilisateur)

Déclarer une action dans ta route

Dans React Router 7, on ajoute simplement une fonction action exportée depuis le même fichier que le loader. Exemple minimal :

app/routes/users.tsx
1
import { Form, useLoaderData, type ActionFunctionArgs }
2
from 'react-router';
3
import { getUsers, addUser } from '~/users.server';
4
5
export async function loader() {
6
return { users: await getUsers() };
7
}
8
9
export async function action({ request }: ActionFunctionArgs) {
10
// 1. lire le payload du formulaire
11
const formData = await request.formData() // ^? FormData API
12
// 2. extraire la donnée
13
const name = formData.get('name') as string
14
// @callout: formData.get peut retourner null si le champ n'existe pas
15
// 3. modifier la DB
16
await addUser({ name })
17
// 4. renvoyer un signe de succès
18
return { ok: true }
19
}
20
21
export default function Users() {
22
const { users } = useLoaderData<typeof loader>()
23
return (
24
<>
25
{/* liste des utilisateurs */}
26
{/* ... */}
27
<Form method="POST">
28
<input name="name" placeholder="Nom d’utilisateur" />
29
<button type="submit">Ajouter</button>
30
</Form>
31
</>
32
)
33
}

Pourquoi Form et method="POST" ?

  • Par défaut un <form> HTML utilise la méthode GET ⇒ déclenche le loader.
  • Avec method="POST", React Router appelle d’abord ton action puis relance le loader pour mettre à jour l’UI.

Le composant <Form> de React Router

React Router fournit un <Form> amélioré :

app/routes/users.tsx
1
<Form method="POST" className="flex gap-2">
2
<input
3
type="text"
4
name="name"
5
className="border px-2"
6
placeholder="Nom d'utilisateur"
7
/>
8
<button type="submit" className="bg-blue-600 text-white px-4">
9
Ajouter un utilisateur
10
</button>
11
</Form>
  • Gestion automatique du chargement optimisé
  • Possibilité d’annuler la requête
  • Recharge partielle ou complète selon ta config

Code serveur : base de données en mémoire

Pour cet exemple, on simule une DB en mémoire dans app/users.server.ts :

app/users.server.ts
1
const db = {
2
users: [
3
{ id: 1, name: 'Virgile' },
4
{ id: 2, name: 'Robert' },
5
]
6
}
7
8
export async function getUsers() {
9
await new Promise(r => setTimeout(r, 100))
10
return db.users
11
}
12
13
export async function addUser({ name }: { name: string }) {
14
const newUser = { id: db.users.length + 1, name }
15
db.users.push(newUser)
16
return newUser
17
}

Attention : à chaque redémarrage du serveur, la mémoire est réinitialisée !
Pour la prod, migre vers une vraie base de données (PostgreSQL, MongoDB, …).


Cycle de vie loader ⇆ action

  1. Chargement initial
    • loader() read
    • render de ton composant
  2. Soumission du formulaire
    • action() write
    • relance de loader() read
    • re-render
  3. Résultat
    • L’UI est toujours en phase avec la DB
1
flowchart LR
2
load[Page load] -->|GET| loader
3
loader --> UI[Render UI]
4
UI -->|POST form| action
5
action --> loader
6
loader --> UI

C’est le principe du full-stack data avec React Router.


Points clés

  • Une mutation se fait toujours côté serveur
  • Déclare export async function action dans ta route
  • <Form method="POST"> déclenche l’action
  • Récupère formData depuis request
  • Retourne un JSON ou redirige (redirect('/users'))
  • Après l’action, le loader se relance et rafraîchit l’UI

Exercices rapides

  1. Supprimer un utilisateur

    • Ajoute un bouton « Supprimer » à chaque ligne de la liste
    • Envoie un formulaire POST avec name="_method" value="delete"
    • Implémente action pour détecter _method === 'delete'
  2. Éditer un utilisateur

    • Sur clic, affiche un champ <input> prérempli
    • Envoie id + name au action
    • Mets à jour db.users avec la nouvelle valeur
  3. Sécuriser l’action

    • Simule une vérification d’admin dans action
    • Si non autorisé, renvoie throw new Response("Forbidden", { status: 403 })
    • Affiche un message d’erreur dans ErrorBoundary

↩︎ Pour approfondir la gestion des formulaires et de la validation, consulte le module Formulaires avancés.

Inclus
Quiz interactifTestez vos connaissances
Validez votre compréhension du module avec notre quiz interactif personnalisé.
1

Comprendre les concepts fondamentaux

Quelle est la principale différence entre les composants client et serveur dans React ?

Les composants client s'exécutent uniquement dans le navigateur
Les composants serveur peuvent utiliser useState
2

Optimisation des performances

Quelle technique est recommandée pour éviter les rendus inutiles dans React ?

Utiliser React.memo pour les composants fonctionnels
Ajouter plus d'états locaux
3

Architecture des données

Quel hook permet de gérer les effets de bord dans un composant React ?

useEffect
useState

Débloquez ce quiz et tous les autres contenus