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.
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.
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 :
D’abord, laissez-moi vous présenter certains termes essentiels de React Query qui m’ont perturbé au début.
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).
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:
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.
Orval prend votre spécification OpenAPI (un fichier YAML ou JSON décrivant votre API) et génère :
queryFn
dans vos hooks React QueryuseQuery
pour récupérer des donnéesuseMutation
pour modifier des donnéesNB: 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*
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.
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 :
Qu’est-ce qui se passe ?
enabled
: !!userId)isLoading
), d’erreur (isError
) et autres, doivent être gérés séparémentMaintenant, combinons les requêtes dans une seule queryFn
:
Qu’est-ce qui se passe?
user.id
pour récupérer les projets, l'utilisateur est d’abord récupéré, puis les projetsuseQuery
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.
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.
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 ?