Gestion optimal des états et des appels API pour la haute disponibilité - Zustand & React Query

1 - Zustand (Client State)
1-1 - Défis avec la gestion des états :
Une notion cruciale dans le développemet des applications modernes est : “Gestion des Etats”, elle peut-être simple à gérer avec des outils natifs comme useContext
ou useState
en React, mais elle devient rapidement une casse-tête quand t-il faut gérer l’état d’une application avec un grand volume de requètes. Rapidement on se retrouve limité, on ajoute des outils par ici et par là et brusquement on se retrouve dans du Provider Hell :
Pire encore, dans le cas de l’utilisation de useState
ou useContext
, on se retrouve facilement dans du re-render inutile (le danger : coûte en ressources) :
Et quand vous choissisez d’adopter des dépendances pour gérer ce que les outils natifs ne vous permettent pas de faire efficacement vous vous confronter à d’autres problème :
Ajouter de dépendances qui alourdi le projet ;
Trop de biolerplate, ce qui rend le code trop complexe et on se voir écrire trop de lignes de code pour des besoins simples.
C’est dans ce contexte que des dévéloppeurs ont mise en place Zustand :
Zustand est une librairie ultra-léger de 3.1 ko qui permet une gestion efficient des états globaux d’une application sans boilerplate. Voici quelques une de ses avantages :
Simplicité d’utilisation :
Pas besoin de Provider, pas d’action, pas de reducer, pas de dispatch. Exemple :
Sélecteurs précis = Re-renders contrôlés :
Seuls les composants qui écoutent une partie précise du state se re-render.
Persistance intégrée (localStorage/sessionStorage) :
Zustand permet de persister automatiquement le state dans le navigateur. Voici un exemple de persistance du token d’auth dans le navigateur :
Pas de provider hell :
Pas besoin de wrapper ton composant d’un store Provider :
Extensible (Middlewares : persist, devtools, immer) :
Ajoute des fonctionnalités avancées sans complexité.
Support SSR, React Native, Vanilla JS :
Fonctionne aussi côté serveur, mobile, ou même sans React. Voici un exemple de stockage d’un état dans un projet React Native sans modification spécifique :
1-2 - Utilisation de Zustand :
Comment créer un store ?
Pour créer un store (état partagé), on va utiliser la méthode
create
:
Comment ce store est utilisé ?
Explication :
Bonus :
Avec Zustand, on peut créer une méthode en dehors de l’état défini, cette méthode sera disponible dans tout les composant pour une utilisation sans nécéssité un hook :
Comment utiliser les middleware ?
Avec les middleware, vous pouvez stocker des informations dans le localStorage pour réutiliser au besoin. Pour stocker des informations, voici comment procéder :
Par défaut, persist de Zustand utilise
localStorage
, ce qui signifie : l’état reste même si l’utilisateur ferme le navigateur ou l’onglet. Si tu souhaites que l’état disparaisse dès que l’onglet ou le navigateur est fermé, il faut utilisersessionStorage
.Comment faire ?
2 - React Query (Server State)
2-1 - Pourquoi React Query ?
React Query a été mise en place pour résoudre un problème simple mais très critiqur pour les applications modernes qui doivent gérer des millions de requéte a une frèquence trés courte.
2-1 - Comment utiliser React Query ?
Fetching simple : GET un profil utilisateur :
Avec React Query, nous utiliserons
useQuery
pour fetch un API, avec queryKey, nous pouvons faire du caching automatique. Voici un exemple :Explication :
On utilise le hook
useQuery
en lui passant deux arguments : une clé unique['user']
qui identifie la requête dans le cache, et la fonctionfetchUser
qui va réaliser l’appel API.React Query retourne automatiquement trois états :
isLoading
qui est vrai si la requête est en cours,error
qui contient l’erreur éventuelle si l’appel échoue, etdata
qui contient les données si l’appel réussit.POST (Créer une ressource) : avec
useMutation
:Contrairement à
useQuery
qui sert à lire des données, ici on utiliseuseMutation
pour exécuter une action qui modifie des données côté serveur.Dans cet exemole, la fonction
createUser
envoie une requête POST vers/api/users
, avec un corps JSON contenant les informations de l’utilisateur.Dans le composant CreateUser, le hook
useMutation
est initialisé avec cette fonction commemutationFn
. Lorsqu’on clique sur le bouton, la fonctionmutation.mutate
déclenche l’appel API avec les données de l’utilisateur.React Query gère alors l’état de la mutation :
isPending
pour afficher un message pendant la création,isSuccess
etisError
pour adapter l’interface en fonction du résultat. Une spécificité importante des mutations avec React Query est la possibilité de faire de l’optimistic update pour une meilleure UX.Refetch automatique + Intervalle :
L’exemple parle de lui même pas besoin d’explication.
Pagination ou Infinite Scroll :
La pagination infinie (infinite scroll ou "Load more") avec React Query se fait grâce au hook
useInfiniteQuery
:La fonction
fetchPosts
récupère une page de posts depuis l’API en utilisant le paramètrepageParam
, qui est fourni automatiquement par React Query.La clé
queryKey
permet d’identifier cette requête dans le cache, tandis que la fonctiongetNextPageParam
indique à React Query comment déterminer la prochaine page à charger, ici via la propriété nextPage renvoyée dans la réponse API.Dans le composant PostList, toutes les pages récupérées sont stockées dans
data.pages
, une liste contenant les résultats de chaque page chargée. On affiche chaque post en parcourant toutes les pages déjà chargées. SihasNextPage
est vrai, cela signifie qu’il existe une autre page à charger, et le bouton « Charger plus » déclenche la fonctionfetchNextPage
pour charger la page suivante.Pré-fetch & cache manual :
La méthode
prefetchQuery
de React Query pour précharger des données dans le cache avant qu’elles ne soient demandées, afin d’accélérer l’affichage futur.D’abord, on récupère l’instance du cache React Query grâce au hook
useQueryClient()
. Ensuite, dans la fonctionprefetchUser
, on utilisequeryClient.prefetchQuery
pour précharger les données de l’utilisateur ayant l’ID 1 en appelant l’API/api/user/1
.Cela stocke la réponse dans le cache sans déclencher de re-render immédiat. Dans l’interface, cette fonction est appelée lorsqu'on survole le bouton grâce à l’événement
onMouseEnter
.Résultat : si l’utilisateur clique ensuite sur un lien ou une page qui utilise cette donnée, React Query n’aura pas besoin de refaire l’appel réseau — la data est déjà dans le cache — ce qui rend l'affichage quasiment instantané. C’est une technique puissante pour anticiper les besoins utilisateurs (ex : hover sur une carte avant ouverture d’un détail) et optimiser la performance perçue de l’application.
Persist cache dans localStorage (Offline Support) :
Tout d’abord, on crée un
queryClient
, qui est le cœur de React Query pour gérer les requêtes et le cache. Ensuite, aveccreateSyncStoragePersister
, on définit un "persister" qui utilisewindow.localStorage
pour stocker le cache de façon synchrone dans le navigateur.Ce persister est ensuite passé à la fonction
persistQueryClient
, qui connecte le cache React Query à ce système de persistance. L’optionmaxAge
précise combien de temps les données doivent être considérées comme valides : ici une heure (en millisecondes).Cela permet de ne pas refetch inutilement les données tant que ce délai n’est pas dépassé. Ainsi, même si l’utilisateur ferme l’application et revient plus tard, les données mises en cache peuvent être restaurées instantanément, améliorant la rapidité et l’expérience utilisateur en mode "quasi offline".
✅ Résumé :
Ressources utiles :
Zustand : https://grafikart.fr/tutoriels/zustand-react-2176
React Query : https://medium.com/@ab19622001/fecthing-and-mutation-with-react-query-v5-73103737aa14
Subscribe to my newsletter
Read articles from Aziz Vorrez directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
