Cours 09 - État global (Zustand)
Développé par Poimandres, Zustand permet de déclarer un état global à l’application (ou plusieurs). Il est ensuite possible de consommer cet état dans n’importe quelle composante client de l’application sans reposer sur le props drilling. React compte aussi sa version native nommée Context. Les deux sont utilisables en parallèle ou en combinaison au besoin.
Contrairement au Context, seules les composantes utilisant la propriété mise à jour effectueront un nouveau rendu. Une modification de n’importe quelle propriété de React Context entraîne un nouveau rendu de toutes les compoosantes qu’il contient.
Installation
Section intitulée « Installation » npm install zustandCréation d’un store
Section intitulée « Création d’un store »On crée un store pour regrouper les propriétés et méthodes relative à un service supplémentaire à ajouter à l’application. On pourrait par exemple en créer un pour la gestion du thème affiché dans l’application et pour les fonctionnalités relatives à l’authentification du côté client.
Dans un dossier _stores, on crée donc un fichier par service.
Répertoiresrc/
Répertoireapp/
Répertoire_stores/
- themeStore.js
En utilisant la fonction create, on crée donc un objet contenant les propriétés et méthodes nécessaires au fonctionnement du service. Le store créé sera utilisé comme un hook React et portera donc le préfixe use:
Propriétés
Section intitulée « Propriétés »import { create } from 'zustand';
const useTheme = create((set) => ({ // propriétés currentTheme: 'dark', themes: ['dark', 'light', 'contrast'], currentThemeIndex: 0,
// méthodes à venir}));Méthodes
Section intitulée « Méthodes »Pour les méthodes devant modifier l’une des propriétés du store (c’est le cas de la plupart des méthodes), on utilise la fonction set récupérée en paramètre. On lui passe un objet ne contenant que les propriétés à modifier et leur nouvelle valeur:
import { create } from 'zustand';
const useTheme = create((set) => ({ // propriétés currentTheme: 'dark', themes: ['dark', 'light', 'contrast'], currentThemeIndex: 0,
// méthodes setTheme: (selection, index) => set({currentTheme: selection, currentThemeIndex: index})}));Si les valeurs actuelles du store sont nécessaires pour obtenir les nouvelles valeurs, on passera plutôt une fonction à set:
import { create } from 'zustand';
const useTheme = create((set) => ({ // propriétés currentTheme: 'dark', themes: ['dark', 'light', 'contrast'], currentThemeIndex: 0,
// méthodes setTheme: (selection) => set((state) => ({ currentTheme: selection, currentThemeIndex: state.themes.findIndex((theme) => theme === selection) ?? -1 })), nextTheme: () => set((state) => { const index = (state.currentThemeIndex + 1) % state.themes.length; return {currentTheme: state.themes[index], currentThemeIndex: index}; })}));Utilisation du store
Section intitulée « Utilisation du store »Il reste ensuite à consommer le store en créant un sélecteur pour chaque propriété ou méthode nécessaire à l’utilisation d’une composante (client):
const MaComposante = () => { // on récupère chaque tranche une à la fois const currentTheme = useTheme(state => state.currentTheme); const nextTheme = useTheme(state => state.nextTheme);
return ( <div> <p>Thème actif: {currentTheme}</p> <button onClick={nextTheme}>Thème suivant</button> </div );};Extension - persist
Section intitulée « Extension - persist »Zustand permet aussi de sauvegarder l’état du store dans le localStorage afin d’assurer une persistence des données. Il faut pour cela transformer la création du store en utilisant l’extension persist.
On devra également:
- donner une nom à la propriété du localStorage sous laquelle on veut sauvegarder l’état
- sélectionner les propriétés à conserver plutôt que de tout sauvegarder (partialize)
import { create } from 'zustand';import { persist, createJSONStorage } from 'zustand/middleware';
const useTheme = create()(persist((set) => ({ // propriétés currentTheme: 'dark', themes: ['dark', 'light', 'contrast'], currentThemeIndex: 0,
// méthodes setTheme: (selection) => set((state) => ({ currentTheme: selection, currentThemeIndex: state.themes.findIndex((theme) => theme === selection) ?? -1 })), nextTheme: () => set((state) => { const index = (state.currentThemeIndex + 1) % state.themes.length; return {currentTheme: state.themes[index], currentThemeIndex: index}; })})), { name: 'nom-dans-localstorage', storage: createJSONStorage(() => sessionStorage), partialize: (state) => ({currentTheme: state.currentTheme, currentThemeIndex: state.currentT})});