Comment charger une donnée côté serveur avec React Router 7

Découvre comment charger des données dynamiques et côté serveur dans ton application React Router 7 avec la méthode loader.

8 min read
Déverrouillez votre potentiel

avec React Router 7

Vous en avez marre de...

❌ perdre du temps à chercher des informations éparpillées
❌ ne pas avoir de retour sur votre progression
Assistant IA spécialisé

Posez vos questions 24/7 à notre IA experte en React Router 7

Quiz interactifs

Validez vos acquis avec des quiz personnalisés et un feedback instantané

🎁 Accès gratuit à cette leçon

Entrez votre email pour débloquer cette leçon gratuite

Dans la leçon précédente, nous avons créé notre première route entièrement statique.

app/routes/users.tsx
1
export default function Users() {
2
return (
3
<div className='px-8 py-2'>
4
<h1 className='text-2xl font-bold'>Utilisateurs</h1>
5
<ul>
6
<li className='text-lg font-mono'>
7
<a href='/'>Virgile</a>
8
</li>
9
<li className='text-lg font-mono'>
10
<a href='/'>Robert</a>
11
</li>
12
<li className='text-lg font-mono'>
13
<a href='/'>John</a>
14
</li>
15
</ul>
16
</div>
17
);
18
}

Les sites webs chargent des données dynamiquement, depuis le serveur. Et ces données varient généralement en fonction de plusieurs paramètres.

C'est logique : Coder en dur chaque donnée produirait un code illisible, non maintenable, et forcerait le développeur à mettre à jour le code source lorsque la donnée change.

Concrètement il y a donc deux problèmes avec notre composant :

  • On n'exploite pas la fonctionnalité principale de React en créant un composant réutilisable
  • On ne charge pas les données côté serveur

Commençons par le premier point.

Créer un composant réutilisable avec ReactJS

Notre composant pourrait être amélioré à deux endroits. Au lieu de coder en dur le nom de nos utilisateurs, déclarons un modèle de donnée, sous forme de tableau d'utilisateurs. Pour l'instant il est toujours codé en dur, mais au moins, il nous permet de boucler sur notre liste d'utilisateurs, et d'en rajouter assez facilement.

app/routes/users.tsx
1
const users = [
2
{ id: 1, name: 'Virgile' },
3
{ id: 2, name: 'Robert' },
4
{ id: 3, name: 'John' }]
5
6
export default function Users() {
7
return (
8
<div className='px-8 py-2'>
9
<h1 className='text-2xl font-bold'>Utilisateurs</h1>
10
<ul>
11
{users.map((user) => (
12
<li key={user.id} className='text-lg font-mono'>
13
<a href='/'>{user.name}</a>
14
</li>
15
))}
16
</ul>
17
</div>
18
);
19
}

Nous avons ajouté un tableau d'utilisateurs, exemple simplifié d'un modèle qu'on pourrait retrouver dans une base de données.

un tableau d'utilisateurs
1
const users = [
2
{ id: 1, name: 'Virgile' },
3
{ id: 2, name: 'Robert' },
4
{ id: 3, name: 'John' },
5
{ id: 4, name: 'Jack' }]

Nous bouclons dessus dans notre composant React. Pour l'instant, nous n'avons pas créé de composant réutilisable, mais nous utilisons la méthode map pour boucler sur chaque élément du tableau.

on boucle sur les utilisateurs
1
<ul>
2
{users.map((user) => (
3
<li key={user.id} className='text-lg font-mono'>
4
<a href='/'>{user.name}</a>
5
</li>
6
))}
7
</ul>

Cela ne règle le problème qu'à moitié : Le composant est toujours codée en dur. Et elle n'est pas chargée côté serveur.

Chargement côté serveur des données avec la méthode loader

React Router possède une page complète expliquant tout ce qu'il est possible de faire dans une route. Dans cette leçon, on va se concentrer sur le chargement côté serveur avec la méthode loader.

L'exemple de la documentation est assez parlant :

Les routes utilisant loader fournissent des données aux composants de route avant qu’ils ne soient rendus. Ils ne sont appelés côté serveur que lors du rendu serveur ou durant la phase de build avec pré‑rendu.

une page
1
export async function loader() {
2
return { message: "Hello, world!" };
3
}
4
5
export default function MyRoute({ loaderData }) {
6
return <h1>{loaderData.message}</h1>;
7
}

Ici, nous voulons afficher une donnée dynamique dans notre composant Users. En ajoutant et en exportant une méthode nommée loader, qui est la convention de React Rouer 7, nous allons pouvoir exécuter du code côté serveur. Dans l'exemple ci-dessus, on renvoie un object basique qu'on affiche ensuite côté client.

Ajout de la méthode loader

La première chose à faire est de déplacer notre tableau d'utilisateurs côté serveur. Nous allons le scoper à l'intérieur de la méthode loader, et le renvoyer côté client.

app/routes/users.tsx
1
import { useLoaderData } from "react-router"
2
export async function loader() {
3
const users = [
4
{ id: 1, name: 'Virgile' },
5
{ id: 2, name: 'Robert' },
6
{ id: 3, name: 'John' }]
7
return { users };
8
}

Nous arrivons au même résultat qu'avant : Nous bouclons sur un tableau d'utilisateurs, sauf que la donnée provient maintenant du serveur. Elle a été chargée dans une méthode loader qui peut être marquée comme asynchrone.

React Router 7 va d'abord exécuter le code de la méthode loader, avant même d'afficher la page web.

Récupération de la donnée côté client

app/routes/users.tsx
1
import { useLoaderData } from "react-router"
2
export default function Users() {
3
const { users } = useLoaderData<typeof loader>();
4
return (
5
<div className='px-8 py-2'>
6
<h1 className='text-2xl font-bold'>Utilisateurs</h1>
7
<ul>
8
{users.map((user) => (
9
<li key={user.id} className='text-lg font-mono'>
10
<a href='/'>{user.name}</a>
11
</li>
12
))}
13
</ul>
14
</div>
15
);
16
}

Il y a deux manières d'accéder à la donnée qui a été chargée côté serveur. Notre composant React est rendu une première fois côté serveur (c'est ce qu'on appelle le SSR, ou Rendu Côté Serveur. Mais il est exécuté une deuxième fois (puis hydraté) côté client.

Nous avons donc besoin d'accéder à la donnée qui a été renvoyée par le serveur depuis la méthode loader.

Pour ce faire, nous utilisons le hook useLoaderData (importé depuis la librairie react-router.

Ce hook nous permet d'accéder à toute la donnée renvoyée par la méthode loader, côté client.

.

Route complète, chargée côté serveur

Voici le composant au complet:

  • React Router 7 détecte la présence de la méthode loader, et va l'exécuter avant même d'afficher le composant.
  • Il détecte ensuite la présence d'un composant React exporté par défaut. Il va donc afficher ce composant côté client.
app/routes/users.tsx
1
import { useLoaderData } from "react-router"
2
export async function loader() {
3
const users = [
4
{ id: 1, name: 'Virgile' },
5
{ id: 2, name: 'Robert' },
6
{ id: 3, name: 'John' }]
7
return { users };
8
}
9
10
export default function Users() {
11
const { users } = useLoaderData<typeof loader>();
12
return (
13
<div className='px-8 py-2'>
14
<h1 className='text-2xl font-bold'>Utilisateurs</h1>
15
<ul>
16
{users.map((user) => (
17
<li key={user.id} className='text-lg font-mono'>
18
<a href='/'>{user.name}</a>
19
</li>
20
))}
21
</ul>
22
</div>
23
);
24
}

Effectivement. La donnée reste codée en dur pour l'instant. Mais nous pouvons néanmoins simuler le comportement d'une base de donnée.

Les fichiers .server.ts

Dans la vidéo, nous créons un nouveau fichier app/users.server.ts qui va contenir toute la logique liée aux utilisateurs.

Cela a de nombreux avantages :

  • C'est une optimisation du code source, qui ne fait charger à l'utilisateur que la logique publique, côté client.
  • C'est pour des raisons de sécurité. Le serveur possède les accès à la base de donnée, et à tous les credentials d'une application web classique, comme des accès à des services externes.
  • C'est avant tout une bonne pratique. Nous n'allons pas écrire tout le code source dans le même composant, qui doit resté visible et simple à maintenir.

Voici le code du fichier que nous avons mis en place ensemble :

app/users.server.ts
1
const db = {
2
users: [
3
{ id: 1, name: 'Virgile' },
4
{ id: 2, name: 'Robert' },
5
{ id: 3, name: 'John' },
6
{ id: 4, name: 'Jack' },
7
],
8
userSettings: [
9
{ id: 1, userId: 1, settings: { theme: 'light' } },
10
{ id: 2, userId: 2, settings: { theme: 'dark' } },
11
{ id: 3, userId: 3, settings: { theme: 'light' } },
12
{ id: 4, userId: 4, settings: { theme: 'dark' } },
13
]
14
}
15
16
export async function getUsers() {
17
18
// Simulate a delay
19
await new Promise(resolve => setTimeout(resolve, 100));
20
return db.users;
21
}

Maintenant, nous avons rajouté un délai artificiel de 100 ms pour simuler un réel appel de base de donnée.

Notre loader n'a plus besoin de déclarer lui-même la donnée codée en dur. Il n'a qu'une seule tâche : appeler la méthode qui charge la donnée, et la renvoyer au client.

Cela donne le code complet, ci dessous :

app/routes/users.tsx
1
import { useLoaderData } from 'react-router';
2
import { getUsers } from '~/users.server';
3
4
export async function loader() {
5
return { users: await getUsers() };
6
}
7
8
export default function Users() {
9
const { users } = useLoaderData<typeof loader>();
10
return (
11
<div className='px-8 py-2'>
12
<h1 className='text-2xl font-bold'>Utilisateurs</h1>
13
<ul>
14
{users.map((user) => (
15
<li key={user.id} className='text-lg font-mono'>
16
<a href='/'>{user.name}</a>
17
</li>
18
))}
19
</ul>
20
</div>
21
);
22
}

Conclusion de la leçon

Pour résumer, dans cette leçon nous avons appris plusieurs concepts :

  • Comment charger une donnée côté serveur avec la méthode loader
  • Comment accéder à la donnée côté client avec useLoaderData
  • Comment rendre notre code plus lisible, en créant une méthode .server.ts contenant toute la logique backend
Premium
Quiz interactif
Testez vos connaissances et validez votre compréhension du module avec notre quiz interactif.
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
Les composants client sont plus rapides
Il n'y a aucune différence significative
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
Éviter d'utiliser les props
Toujours utiliser les class components
3

Architecture des données

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

useEffect
useState
useMemo
useContext
4

Gestion des erreurs

Comment implémenter la gestion des erreurs pour les requêtes API dans React ?

Utiliser try/catch avec async/await
Ignorer les erreurs
Toujours afficher un message d'erreur
Rediriger l'utilisateur
5

Déploiement et CI/CD

Quelle est la meilleure pratique pour déployer une application React en production ?

Utiliser un service CI/CD comme GitHub Actions
Copier les fichiers manuellement via FTP
Envoyer le code source complet
Ne jamais mettre à jour l'application

Débloquez ce quiz et tous les autres contenus premium en achetant ce cours