đ Gestion des mutations avec useOptimistic
dans Next.js 13+
Lâoptimistic UI amĂ©liore lâexpĂ©rience utilisateur en affichant immĂ©diatement une mise Ă jour de lâinterface avant mĂȘme la fin du traitement serveur.
Dans Next.js 13+, on utilise useOptimistic
pour gérer ces mutations cÎté client tout en tirant parti des Server Actions.
1ïžâŁ Pourquoi utiliser useOptimistic
?
â Avantages :
- Affichage instantané des mises à jour sans attendre la réponse serveur.
- Meilleure expérience utilisateur (réactivité accrue).
- Evite les flashs de chargement entre lâenvoi et la mise Ă jour rĂ©elle.
â Sans useOptimistic
:
- Lâinterface attend la rĂ©ponse serveur avant de sâactualiser â latence perçue.
- Besoin de rafraßchir les données via
revalidatePath()
aprĂšs chaque mutation.
2ïžâŁ Exemple : Liste de tĂąches avec mise Ă jour optimiste
Nous allons créer une application de gestion de tùches avec :
1ïžâŁ Un affichage en temps rĂ©el des tĂąches.
2ïžâŁ Une suppression optimiste avant confirmation serveur.
đ Ătape 1 : Server Actions pour rĂ©cupĂ©rer et supprimer une tĂąche
"use server";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
// Récupérer les tùches
export async function getTodos() {
return await db.todo.findMany();
}
// Supprimer une tĂąche
export async function deleteTodo(id: string) {
await db.todo.delete({ where: { id } });
// Forcer la mise à jour cÎté serveur
revalidatePath("/todos");
}
â Explication :
getTodos()
récupÚre les tùches.deleteTodo()
supprime une tĂąche et rafraĂźchit automatiquement la liste.
đ Ătape 2 : Affichage avec useOptimistic
"use client";
import { useOptimistic, useState } from "react";
import { getTodos, deleteTodo } from "@/app/actions/todos";
export default function TodosPage() {
const [todos, setTodos] = useState(await getTodos());
// đŻ Utilisation de `useOptimistic`
const [optimisticTodos, setOptimisticTodos] = useOptimistic(
todos, // Valeur initiale
(currentTodos, idToRemove) => currentTodos.filter(todo => todo.id !== idToRemove)
);
async function handleDelete(id: string) {
// Mise à jour immédiate de la liste (optimiste)
setOptimisticTodos(id);
// Envoi réel au serveur
await deleteTodo(id);
}
return (
<div className="max-w-md mx-auto p-4 border rounded-lg shadow-lg">
<h1 className="text-xl font-bold mb-4">Liste des tĂąches</h1>
<ul>
{optimisticTodos.map((todo) => (
<li key={todo.id} className="flex justify-between border-b py-2">
{todo.title}
<button
onClick={() => handleDelete(todo.id)}
className="text-red-500"
>
â
</button>
</li>
))}
</ul>
</div>
);
}
â Explication :
useOptimistic(todos, callback)
permet de supprimer immĂ©diatement une tĂąche de la liste.- DĂšs le clic, la tĂąche disparaĂźt avant mĂȘme que
deleteTodo()
soit exĂ©cutĂ©. - Si la suppression Ă©choue, on peut gĂ©rer un rollback (ex. afficher un message dâerreur).
3ïžâŁ Comparaison : useOptimistic
vs revalidatePath
đč MĂ©thode | useOptimistic | revalidatePath |
---|---|---|
đŻ Effet | Mise Ă jour immĂ©diate | RafraĂźchit la page aprĂšs mutation |
⥠Performance | Instantané | Plus lent (attend la réponse serveur) |
đ ExpĂ©rience utilisateur | Ultra rĂ©actif | Parfois des flashs de chargement |
â Utilisation idĂ©ale | Modifications visuelles rapides | DonnĂ©es critiques nĂ©cessitant une revalidation |
4ïžâŁ GĂ©rer les erreurs (rollback en cas dâĂ©chec)
Que se passe-t-il si la suppression Ă©choue ?
On peut ajouter une gestion dâerreur avec un rollback.
async function handleDelete(id: string) {
const previousTodos = optimisticTodos; // Sauvegarde
setOptimisticTodos(id); // Suppression instantanée
try {
await deleteTodo(id);
} catch (error) {
setOptimisticTodos(previousTodos); // Rollback en cas d'erreur
}
}
â
Si deleteTodo(id)
échoue, la tùche réapparaßt immédiatement.
đŻ Conclusion : Pourquoi useOptimistic
est un game-changer ?
đ Avec useOptimistic
, lâUI devient ultra-fluide :
â
Moins de latence perçue â meilleures performances UX.
â
Moins de re-rendering inutile.
â
Gestion fine des mutations sans bloquer lâinterface.