Comment charger une donnée dynamique côté client
Dans cette leçon on va apprendre à combiner client loader, action, hydrateFallback, Suspense et revalidate pour une UX rapide et cache SSR/SPA.
Dans React Router 7, le chargement client permet de récupérer des données après l’hydratation initiale, sans recharger toute la page ni retomber dans un rendu serveur (SSR). Tu utilises :
export async function clientLoaderdans tes modules de route- le hook
useLoaderDatapour accéder à la promesse renvoyée <Suspense>et<Await>pour afficher un squelette avant le rendu
Cette approche améliore l’expérience utilisateur (UX) en évitant le flash de chargement et en accélérant la navigation.
Principe du clientLoader
Comme le loader côté serveur, le clientLoader est une fonction exportée depuis un module de route. Elle :
- Récupère une promesse fournie par le
serverLoader. - Peut mettre en cache la donnée côté client.
- Renvoie un objet
{ users: User[] }.
1import { useLoaderData } from "react-router"23// 1. loader serveur : renvoie une promesse4export async function loader() {5return { usersPromise: getUsers() } // getUsers() -> Promise<User[]>6}78// 2. Hydratation initiale : on stocke la donnée côté client9export async function clientLoader({ serverLoader }) {10console.log("trigger client loader")11const { usersPromise } = await serverLoader()12return { users: await usersPromise }13}
Tip
clientLoader.hydrate = true indique à React Routerd’exécuter le
clientLoader dès l’hydratation, sans attendre la navigation.Rendu avec <Suspense> et <Await>
Pour exploiter la promesse côté client, tu combines :
- Un
<Suspense fallback={…}>: affiche le squelette tant que la promesse n’est pas résolue. - Un
<Await resolve={promise}>: récupère et injecte la donnée dans ton JSX.
1import { Suspense } from "react"2import { Await, useLoaderData } from "react-router"34export default function Users() {5// Promise<User[]>6const { usersPromise } = useLoaderData<typeof loader>()78return (9<div className="px-8 py-2">10<h1 className="text-2xl font-bold">Utilisateurs</h1>1112<Suspense13fallback={14<ul>15<li className="h-6 w-3/4 bg-gray-300 rounded animate-pulse mb-2" />16<li className="h-6 w-2/3 bg-gray-300 rounded animate-pulse mb-2" />17<li className="h-6 w-4/5 bg-gray-300 rounded animate-pulse mb-2" />18<li className="h-6 w-3/5 bg-gray-300 rounded animate-pulse" />19</ul>20}21>22<Await resolve={usersPromise}>23{(users) => (24<ul>25{users.map((user) => (26<li key={user.id} className="text-lg font-mono">27{user.name}28</li>29))}30</ul>31)}32</Await>33</Suspense>34</div>35)36}
Pourquoi fallback dans Suspense ?
Le composant fallback s’affiche instantanément côté client,
sans attendre le second rendu hydraté.
Contrôler la révalidation avec shouldRevalidate
Par défaut, chaque navigation relance le loader et le clientLoader.
Pour désactiver ce comportement, tu peux exporter :
1export function shouldRevalidate({ nextLocation, formMethod }) {2// Toujours false ⇒ pas de rechargement automatique3return false4}
1export function shouldRevalidate(arg) {2- return true3+ return false4}
Tip
Utile pour les données statiques ou déjà mises en cache par le serveur.
Points clés
clientLoadercomplète leloaderSSR pour charger après l’hydratation.<Suspense>+<Await>affichent un squelette pendant le chargement client.clientLoader.hydrate = truedéclenche dès l’hydratation initiale.useLoaderData<typeof loader>()fournit la promesse côté client.shouldRevalidatete permet de stabiliser ou forcer la ré-exécution.
Exercices rapides
-
Pagination simple
- Modifie
getUsers()pour accepterpage: number. - Appelle
clientLoaderavec le paramètre?page=…et affiche
“Précédent / Suivant”.
- Modifie
-
Gestion d’erreur client-side
- Dans
usersPromise, lance une exception pour
page > 3. - Affiche un message d’erreur personnalisé avec
<ErrorBoundary>.
- Dans
-
Skeleton avancé
- Crée un composant
<UserSkeleton />réutilisable. - Affiche 5 squelettes au lieu de 4 pendant le fallback.
- Crée un composant
Comprendre les concepts fondamentaux
Quelle est la principale différence entre les composants client et serveur dans React ?
Optimisation des performances
Quelle technique est recommandée pour éviter les rendus inutiles dans React ?
Architecture des données
Quel hook permet de gérer les effets de bord dans un composant React ?