Créer un hook réutilisable pour récupérer l'utilisateur connecté
Apprends à centraliser la récupération de l’utilisateur connecté avec un hook React Router 7 simple et performant pour éviter les requêtes redondantes.
Arrêter de dupliquer le loader
Chaque fois que tu veux connaître l’utilisateur connecté, tu appelles la
même fonction dans le loader de la route courante ?
1export async function loader({ request }: LoaderArgs) {2return { user: await getOptionalUser({ request }) }3}
Ça marche, mais :
- la requête part trois fois si ta page empile trois routes imbriquées ;
- tu termines avec autant de
loadercopiés/collés que de pages.
On peut faire mieux : charger l’utilisateur une seule fois dans
root.tsx, puis l’exposer partout via un hook maison.
Le plan en trois étapes
- Charger l’utilisateur dans le
loaderdeapp/root.tsx. - Lire cette data depuis n’importe quelle route avec
useRouteLoaderData(). - Envelopper le tout dans
useOptionalUser()pour une API ultra-simple.
1 – Charger l’utilisateur à la racine
1import { data, type LoaderFunctionArgs } from "react-router"2import { getOptionalUser } from "~/server/sessions.server"34export async function loader({ request }: LoaderFunctionArgs) {5const user = await getOptionalUser({ request }) // ↙︎ DB + cookie6return data({ user }) // { user | null }7}
La route root est toujours montée, donc ce loader tourne exactement
une fois par navigation.
2 – Récupérer la data via useRouteLoaderData
react-router expose un hook miracle :
1useRouteLoaderData<typeof rootLoader>("root")
root= l’ID de la route ciblée- le type générique reprend celui du
loader⇒ aucune conversion manuelle
3 – Emballer tout ça dans un hook réutilisable
1import { useRouteLoaderData } from "react-router"2import type { loader as rootLoader } from "./root.tsx"34export function useOptionalUser() {5const data = useRouteLoaderData<typeof rootLoader>("root")6return data?.user ?? null // 👈 renvoie User | null7}
Et voilà !
Tu viens de créer un « contexte » sans React.createContext, sans
useState, sans provider. React Router se charge de la distribution.
Utilisation dans n’importe quelle page
1import { useOptionalUser } from "~/root"23export default function Home() {4const user = useOptionalUser()56return (7<h1 className="text-2xl font-bold">8{user ? `Salut ${user.firstName}` : "Bienvenue 👋"}9</h1>10)11}
Aucun loader, zéro requête : le composant lit directement la donnée déjà chargée.
Zoom : comment React Router propage les données ?
- Au premier rendu SSR ou lors d’une navigation, React Router exécute
tous les
loaderdes routes actives (root → layout → page). - Les résultats sont stockés dans un cache interne.
useRouteLoaderData()lit simplement ce cache.
Résultat : même si trois composants lisent useOptionalUser(),
l’appel base de données ne part qu’une fois.
Et si je veux partager une donnée dans un layout ?
Répète exactement la même stratégie !
1export async function loader() {2return { users: await getUsers() }3}45export function useUsersData() {6const data = useRouteLoaderData<typeof loader>("routes/users+/_usersLayout")7if (!data) throw new Error("useUsersData must be used inside UsersLayout")8return data.users9}
Toutes les routes enfants (/users, /users/:slug, …) peuvent alors
appeler useUsersData() sans re-requêter la DB.
Gérer le cas « hook utilisé hors contexte »
Hors du layout, useRouteLoaderData() renvoie undefined.
Deux options :
- Tolérant :
return data?.users ?? [] - Strict :
if (!data) throw new Error("…")Tu seras immédiatement averti si tu importes le hook au mauvais endroit.
Bonus : un hook TypeScript-friendly
Pour éviter de retaper le nom de la route, déclare l’ID en variable :
1export const ROOT_ID = "root"23export async function loader() { … }45export function useOptionalUser() {6return useRouteLoaderData<typeof loader>(ROOT_ID)?.user ?? null7}
Si tu renommes ta route, un simple Find & Replace suffira.
Points clés à retenir
- Charge à la racine, lis partout : un seul hit DB pour toute l’appli.
useRouteLoaderData()lit la data d’une route précise ; tu peux l’utiliser à n’importe quelle profondeur.- Un hook custom rend l’API lisible (
useOptionalUser()) et garde un typage strict (User | null). - Plus besoin de
React.Contextpour diffuser des données chargées côté serveur. - Pour les données relatives à un sous-layout, crée un second hook
useXXXData()sur le même modèle
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 ?