Local useQuery vs Custom Hook

Pourquoi je ne vous conseille pas d'utiliser useQuery directement dans la page.
Développeurs React bonjour !
Je vois souvent passer des posts sur les réseaux sociaux (Linkedin) de développeurs très content de découvrir la vie (je plaisante)... React-Query, et c'est une bonne chose !
Je suis très heureux que l'outil soit de plus en plus adapté car il permet énormément de choses:
- notamment l'optimisation des appels et du caching
- la mise en place du DRY (Don't Repeat Yourself)
- même de faire une approche temps réel si on arrive à bien gérer le cache
Malheureusement trop de développeurs font encore l'erreur d'utiliser React-Query directement dans la page et ce n'est pas la façon la plus optimale de s'en servir.
Le problème de la plupart des pages React
Je pars du principe que vous connaissez déjà l'outil et que vous avez déjà fait votre wrapper de QueryProvider etc...
Voici à quoi ressemble une page qui utilise React-Query la plupart du temps:
'use client'
import { useQuery } from '@tanstack/react-query'
type Fruit = {
id: number
name: string
}
export default function FruitsPage() {
const { data, isLoading, error } = useQuery({
queryKey: ['fruits'],
queryFn: async (): Promise<Fruit[]> => {
await new Promise((r) => setTimeout(r, 500))
return [
{ id: 1, name: 'Pomme' },
{ id: 2, name: 'Banane' },
{ id: 3, name: 'Fraise' },
]
},
})
if (isLoading) return <p>Chargement...</p>
if (error) return <p>Erreur lors du chargement</p>
return (
<div>
<h1>Liste des fruits</h1>
<ul>
{data?.map((fruit) => (
<li key={fruit.id}>{fruit.name}</li>
))}
</ul>
</div>
)
}
Et ce composant fonctionne. On peut penser que tout est bon et que l'on peut continuer tranquillement notre développement.
Mais ! Ce n'est absolument pas le cas, et vous passez à côté de quelque chose d'essentiel.
Mettre en place les bonnes pratiques avec React
Lorsque vous travailler sur un projet qui consomme une API, il n'est pas rare que chaque endpoint soit utilisé à plusieurs endroit et de différentes manières.
Et, à ce moment, se poseront les questions suivantes:
- Comment fait on la SOC (Separation of Concern) pour avoir le code vue quelque part et les requêtes ailleurs ?
- Comment fait on pour mettre en place le DRY (Don't Repeat Yourself) ?
Et si notre objectif est de faire du code maintenable, il vaudrait mieux réduire la masse de code et factoriser le tout dans une fonction.
Et React nous permet de le faire via les Hooks.
On va mettre en place la même chose maintenant mais via différents fichiers afin d'isoler la requête de la page.
On crée un fichier hooks/useGetFruits.ts
// hooks/useGetFruits.ts
import { useQuery } from '@tanstack/react-query'
type Fruit = {
id: number
name: string
}
export function useGetFruits() {
return useQuery({
queryKey: ['fruits'],
queryFn: async (): Promise<Fruit[]> => {
await new Promise((r) => setTimeout(r, 300))
return [
{ id: 1, name: 'Pomme' },
{ id: 2, name: 'Banane' },
{ id: 3, name: 'Fraise' },
]
},
})
}
Et enfin dans notre page, on n'a plus qu'à appeler le hook:
'use client'
import { useGetFruits } from '@/hooks/useGetFruits'
export default function FruitsPage() {
const { data, isLoading, error } = useGetFruits()
if (isLoading) return <p>Chargement...</p>
if (error) return <p>Erreur lors du chargement</p>
return (
<div>
<h1>Liste des fruits</h1>
<ul>
{data?.map((fruit) => (
<li key={fruit.id}>{fruit.name}</li>
))}
</ul>
</div>
)
}
Voilà, vous venez de faire la même chose en respectant 2 principes essentiels pour maintenir votre codebase.
De même, l'utilisation de cette même requête dans un composant de pagination par exemple, sera d'autant plus simple, il faudra juste appeler le hook useGetFruits().
Respecter ce principe est important, les devs juniors le prennent à la légère mais lorsque le code fera plus de 15 000 lignes, vous me remercierez.
Bon code !

Alexandre P.
Développeur passionné depuis plus de 20 ans, j'ai une appétence particulière pour les défis techniques et changer de technologie ne me fait pas froid aux yeux.
Poursuivre la lecture dans la rubrique Dev