Découvre comment charger des données dynamiques et côté serveur dans ton application React Router 7 avec la méthode loader.
avec React Router 7
Posez vos questions 24/7 à notre IA experte en React Router 7
Validez vos acquis avec des quiz personnalisés et un feedback instantané
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.
1export default function Users() {2return (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 :
Commençons par le premier point.
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.
1const users = [2{ id: 1, name: 'Virgile' },3{ id: 2, name: 'Robert' },4{ id: 3, name: 'John' }]56export default function Users() {7return (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.
1const 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.
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.
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.
1export async function loader() {2return { message: "Hello, world!" };3}45export default function MyRoute({ loaderData }) {6return <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.
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.
1import { useLoaderData } from "react-router"2export async function loader() {3const users = [4{ id: 1, name: 'Virgile' },5{ id: 2, name: 'Robert' },6{ id: 3, name: 'John' }]7return { 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.
1import { useLoaderData } from "react-router"2export default function Users() {3const { users } = useLoaderData<typeof loader>();4return (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.
<typeof loader>
signifie qu'on va lier le type de cet objet, au type inféré que renvoie notre méthode loaderVoici le composant au complet:
1import { useLoaderData } from "react-router"2export async function loader() {3const users = [4{ id: 1, name: 'Virgile' },5{ id: 2, name: 'Robert' },6{ id: 3, name: 'John' }]7return { users };8}910export default function Users() {11const { users } = useLoaderData<typeof loader>();12return (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.
.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.
.server.ts
ne seront pas compilés côté client. Ces fichiers ne peuvent être exécuté uniquement côté serveur.Cela a de nombreux avantages :
Voici le code du fichier que nous avons mis en place ensemble :
1const db = {2users: [3{ id: 1, name: 'Virgile' },4{ id: 2, name: 'Robert' },5{ id: 3, name: 'John' },6{ id: 4, name: 'Jack' },7],8userSettings: [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}1516export async function getUsers() {1718// Simulate a delay19await new Promise(resolve => setTimeout(resolve, 100));20return 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 :
1import { useLoaderData } from 'react-router';2import { getUsers } from '~/users.server';34export async function loader() {5return { users: await getUsers() };6}78export default function Users() {9const { users } = useLoaderData<typeof loader>();10return (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}
Pour résumer, dans cette leçon nous avons appris plusieurs concepts :
.server.ts
contenant toute la logique backendQuelle est la principale différence entre les composants client et serveur dans React ?
Quelle technique est recommandée pour éviter les rendus inutiles dans React ?
Quel hook permet de gérer les effets de bord dans un composant React ?
Comment implémenter la gestion des erreurs pour les requêtes API dans React ?
Quelle est la meilleure pratique pour déployer une application React en production ?