5
minutes
Mis à jour le
27/2/2025


Share this post

Découvrez les meilleures pratiques de React Query pour optimiser vos applications : gestion du cache, hooks autogénérés, transformation de données et prefetching pour une expérience utilisateur fluide.

#
React
Pierre Tardif
Software Engineer

Introduction

J’utilise React Query depuis plusieurs années, que ce soit pour des side projects ou des applications en production. Cela a été pour moi un changement de paradigme. Aujourd’hui, je l’utilise dans toutes les applications que je développe avec React.

Grâce à son approche simple mais puissante de gérer les données asynchrones, React Query a explosé en popularité et est utilisé dans plus de 20% des applications React aujourd’hui. Cependant, les développeurs ne l’utilisent pas à son plein potentiel.

Source

Dans cet article, j’ai listé les apprentissages que j’aurais aimé connaître avant de déployer ma première application React Query en production. Que vous soyez en train d’apprendre React Query, ou que vous l’utilisiez depuis longtemps, ces pratiques vous aideront à maintenir une codebase plus propre, à réduire les bugs et à améliorer les performances globales de votre application.

NB : je ne vais pas montrer la configuration ou expliquer les fonctionnalités de React Query en détail dans cet article. Je suppose que vous avez déjà travaillé avec. Sinon, je vous invite à parcourir la documentation de React Query et cette liste de tutoriels sur le blog de TkDodo.

Qu’est ce React Query ?

React Query est une librairie de gestion d’états asynchrones de votre application React. C’est un terme un peu barbare pour dire que React Query gère toute la complexité des états asynchrones. Elle fournit une approche déclarative pour récupérer, mettre en cache, synchroniser et mettre à jour les données du serveur. Vous n’avez qu’à lui passer une promesse, et React Query vous retourne vos données.

Grâce à son architecture back-end agnostique, React Query peut être utilisée avec une API Rest, GraphQL, ou n’importe quelle autre architecture d’API.

Voici un exemple de récupération et d'affichage de données provenant d’un serveur sans React Query :

Bien que cette approche fonctionne, elle nécessite une gestion manuelle du cycle de vie et de l'état du composant, ce qui peut entraîner une duplication de code et des bugs potentiels.

Avec le hook React Query useQuery, vous pouvez supprimer le “useEffect” et automatiser la gestion du cycle de vie des données :

La différence entre staleTime et gcTime (ou cacheTime)

D’abord, laissez-moi vous présenter certains termes essentiels de React Query qui m’ont perturbé au début.

  • Le terme Stale est utilisé pour qualifier une donnée lorsqu’elle est obsolète. Dans React Query, les données stale sont toujours affichées à l’utilisateur pendant que de nouvelles données sont en train de charger.

Dans cet exemple, la donnée retournée par useQuery devient stale après 5 minutes. Le staleTime est la durée avant que les données soient considérées obsolètes et doivent être récupérées à nouveau (refetching).

  • Le Cache est l’endroit où React Query stocke les données de votre application front-end. Les données restent dans le cache jusqu'à ce qu'elles soient dans les deux cas suivants :
    1. Elles sont inactives (aucun composant ne les utilise)
    2. Elles n’ont pas été utilisées pendant la durée de gcTime (garbage collect time)

Dans ce composant TodoList, la requête est active parce que le composant l’utilise. Dès que la TodoList est “unmount” (si l’utilisateur change de page), la requête devient alors “inactive”.  Si l’utilisateur ne revient pas sur la page où ce composant est rendu dans les 5 minutes (gcTime), la donnée est effacée du cache. En revanche, s’il revient dans les 5 minutes, les données auront été conservées dans le cache.

Dans certains cas, vous souhaitez que les données ne soient jamais effacées du cache. Par exemple, si les données récupérées du back-end ne sont jamais modifiées par l’utilisateur (e.g. de la configuration, les en-têtes d’un tableau). Pour ça, React Query propose de définir le staleTime et le gcTime à Infinity :

Vous me direz, à quoi servent les trois autres attributs:

  • refetchOnWindowFocus: false - empêche la requête d’être exécutée à nouveau lorsque la fenêtre est focalisée
  • refetchOnMount: false - garantit que la requête n’est pas exécutée lorsque le composant est “remount” et continue d’utiliser les données mises en cache
  • refetchOnReconnect: false - empêche la requête d’être exécutée à nouveau lorsque l'utilisateur se reconnecte au réseau

L’autogénération des hooks, des fonctions de data fetching et des types

Maintenir la cohérence des contrats d’interfaces entre le front-end et le back-end peut être souvent challengeant. Si vous utilisez React Query, vous devez maintenir manuellement l’unicité de vos Query Keys à travers toute votre application.

Des librairies comme query-key-factory implémentent un standard qui gère la création des Query Keys pour vous. Seulement, vous devrez malgré tout créer vos useQuery hooks manuellement, ce qui est assez répétitif. Ainsi, si vous utilisez une API REST, je vous conseille d’utiliser des outils de génération de code client comme Orval qui s’appuie sur les spécifications OpenAPI, générés par votre back-end.

Pourquoi utiliser Orval ?

  • Élimine le code boilerplate: au lieu d'écrire du code répétitif à la main, Orval le génère automatiquement pour vous. Les Query Keys, les Query fonctions et les hooks seront automatiquement générés
  • Réduit le risque d’erreurs: en se basant sur la spécification OpenAPI comme source de vérité, vous conservez la cohérence de votre code et facilitez les mises à jour des fonctions et des types
  • Assure un typage fort: grâce à TypeScript, Orval génère du code entièrement typé, utilisable directement dans le front-end

Que fait Orval ?

Orval prend votre spécification OpenAPI (un fichier YAML ou JSON décrivant votre API) et génère :

  • les Types (si vous utilisez TypeScript) : pour les DTO sortants (OutboundDto) et entrants (InboundDto)
  • les fonctions de data fetching: encapsulant l’appel API, déjà typées, et à passer en argument de l’attribut queryFn dans vos hooks React Query
  • les Hooks React Query :
    • useQuery pour récupérer des données
    • useMutation pour modifier des données
NB: Si vous utilisez GraphQL avec React Query, vous pouvez utiliser GraphQL Code Generator pour générer automatiquement vos types Typescript et vos React Query hooks dans votre front-end*

Comment transformer les données d’un useQuery

Dans la plupart des applications, vous devez filtrer ou trier les données que vous récupérez de votre API et vous voulez que ces données restent synchronisées avec le cache de React Query, par exemple lorsqu’une mutation arrive.

Une façon naïve de le faire est avec useMemo :


React Query renvoie les données brutes provenant du cache. Ensuite, dans le composant, la transformation est mémorisée en utilisant useMemo, afin de ne pas recalculer la valeur de completedTodos tant que data ne change pas.

React Query propose un attribut select qui permet d’appliquer des transformations sur les données juste après qu’elles soient récupérés du cache. Ainsi, le cache de *todos* n’est pas modifié car le select agis au niveau du useQuery

L’avantage est que useCompletedTodos peut être utilisé dans n’importe quel composant. Ainsi, la logique de filtrage est centralisée à un seul endroit.

Quand utiliser useMemo ?

Pour des besoins très ciblés, propres au contexte local d’un composant. Avec useMemo, vous avez quand même un render pass (le composant réagit au changement de référence de la donnée), même si la transformation est mémorisée.

Quand utiliser select ?

Pour des transformations qui ont du sens au niveau “domaine” (filtrer, trier, mettre en forme), surtout si elles sont réutilisées à plusieurs endroits. Avec select, React Query ne notifiera votre composant que si le résultat transformé est effectivement différent.

Combinez plusieurs appels d’API pour un seul état UI

React Query prend en charge les requêtes dépendantes, c’est-à-dire lorsque l’exécution d’une requête dépend des données renvoyées par une autre requête. C’est particulièrement utile lorsque vous ne contrôlez pas les API consommées dans le front-end et que vous devez combiner plusieurs appels d’API pour obtenir un seul état.

Voici un exemple qui montre comment React Query peut gérer des requêtes dépendantes :

  • l’objectif est de récupérer la liste des projets d’un utilisateur
  • l’hypothèse est qu’il n’existe pas d’endpoint pour récupérer la liste des projets d’un utilisateur par son email, seulement un endpoint pour récupérer  cette liste à partir de son id

Qu’est-ce qui se passe ?

  • La première requête récupère les informations de l’utilisateur par son email. Ensuite, la deuxième requête qui récupère ses projets s'exécute (enabled: !!userId)
  • Les états de chargement (isLoading), d’erreur (isError) et autres, doivent être gérés séparément

Maintenant, combinons les requêtes dans une seule queryFn :

Qu’est-ce qui se passe?

  • Étant donné que nous avons besoin du user.id pour récupérer les projets, l'utilisateur est d’abord récupéré, puis les projets
  • Les projets sont retournés par une seule useQuery
  • L'interface utilisateur ne traite plus qu’un seul état de chargement (isLoading) et un seul état d'erreur (isError)

En combinant les queryFn, React Query ne retourne que les états d’un seul hook ce qui rend la gestion des états de chargement, d’erreur et de succès beaucoup plus simple.

Ainsi, si votre UI n’affiche conceptuellement qu’une seule donné, il est souvent plus simple de combiner les queryFn pour n’avoir à gérer que les états d’une seule requête.

Utiliser le “prefetching” pour améliorer l'expérience utilisateur

Je vois encore beaucoup d’applications qui n’anticipent pas le chargement de leurs pages. Par exemple, pour les applications e-commerce, le temps de chargement d’une page produits est critique.

Le “prefetching” consiste à récupérer les données avant que l'utilisateur n'en ait besoin. Ce n’est pas un nouveau concept qui est venu avec React Query, il existe depuis longtemps.

Quand avez-vous besoin de “prefetcher” de la donnée ? Lorsque l’utilisateur en aura besoin parce qu’il va naviguer sur une nouvelle page. S'il survole un produit d’un site e-commerce, vous pouvez “prefetcher” les données du produit. Ainsi, lorsque l’utilisateur navigue vers la page produit, les données sont déjà disponibles. L’utilisateur ne voit pas de chargement. Il n’y a rien à changer dans le code, l'expérience est fluide.

Cependant, tout dépend de la façon dont votre cache est configuré. Avec React Query, tout ce qui est stale est servi instantanément lorsque les données seront demandées. Bien sûr, si votre gcTime est nul (ce qui ne devrait pas être le cas), alors le “prefetch” ne fonctionnera jamais. Pourquoi ? Parce que vous stockez des données dans le cache pour une requête qui n'est pas active.

Conclusion

A travers cet article, nous avons pu voir plusieurs cas concrets d’utilisation de React Query. Cette liste n’est pas exhaustive, mais elle peut vous aider à améliorer la performance de votre application React et de réduire les bugs potentiels qui pourraient être causés par React Query.

Quelles sont les bonnes pratiques que vous mettez en place sur vos projets React Query ?