MAISON CODE .
/ Tech · React · State Management · Architecture · Performance

Gestion de l’État : l’atomique contre le monolithe

Redux est (pour la plupart) mort. Le contexte est un piège. Une plongée technique approfondie dans la gestion moderne de l'état React : Zustand, Jotai et TanStack Query.

AB
Alex B.
Gestion de l’État : l’atomique contre le monolithe

Au cœur de chaque application React se trouve « l’État ». Si l’interface utilisateur est fonction de l’état (€UI = f(state)€), alors la gestion de cet état est la décision technique la plus critique que vous prenez.

Pendant des années, nous avons eu Redux. C’était verbeux, centralisé et immuable. Cela a fonctionné, mais vous avez écrit plus de passe-partout que de code. Ensuite, nous avons eu Contexte. C’était intégré et simple. Nous nous sommes tous précipités pour l’utiliser. Ensuite, nos applications ont commencé à ralentir. La saisie d’une saisie de texte provoquait le nouveau rendu de la barre latérale. Nous avons réalisé : Context est un outil d’injection de dépendances, pas un outil de gestion d’état.

En 2025, l’écosystème a mûri. Nous classons maintenant l’état en trois couches distinctes, chacune dotée d’un outil spécialisé.

Pourquoi Maison Code en parle

Nous avons vu l’application d’un client s’afficher à nouveau 4 000 fois par seconde grâce à un seul fournisseur de contexte. Nous les avons déplacés vers une architecture d’état à trois couches :

  • État du serveur : requête TanStack (pour la mise en cache et l’hydratation).
  • État global du client : Zustand (pour le panier et le thème).
  • Local State : Signaux ou useState (pour les entrées de formulaire). Cela a réduit le temps de blocage du thread principal de 600 ms et amélioré les scores INP à <50 ms. Nous pensons que la gestion de l’état est la première cause de perte de performances dans les applications React.

La théorie : les trois couches

1. État du serveur (le cache)

Données qui résident sur un serveur distant. Vous ne le « possédez » pas ; vous l‘“empruntez”. Cela peut devenir obsolète.

  • Exemples : profil utilisateur, liste de produits, commandes.
  • Outil : TanStack Query (React Query) ou SWR.
  • Anti-Pattern : mise manuelle des données API dans Redux/Zustand. Vous finissez par réinventer les états de mise en cache, de déduplication et de chargement.

2. État du client (l’interface utilisateur)

Données qui existent uniquement dans le navigateur et sont partagées entre les composants.

  • Exemples : Le modal est-il ouvert ? Quel est le thème actuel ? Qu’y a-t-il dans le panier ?
  • Outil : Zustand (pour les magasins mondiaux) ou Jotai (pour les mises à jour atomiques).

3. État local (le composant)

Données isolées sur un seul composant ou ses enfants directs.

  • Exemples : La liste déroulante est-elle ouverte ? Quelle est la valeur de ce champ de saisie spécifique ?
  • Outil : useState / useReducer.

Le problème du contexte

Pourquoi ne pas simplement utiliser « createContext » pour tout ? En raison de la réactivité à gros grains.

const DataContext = createContext();

function Fournisseur ({ enfants }) {
  const [nom, setName] = useState("Alex");
  const [thème, setTheme] = useState("Dark");
  
  // Cette référence d'objet change à TOUTE mise à jour
  const value = { nom, thème, setName, setTheme } ; 

  return <DataContext.Provider value={value}>{children}</DataContext.Provider> ;
}

Si setName("John") est appelé :

  1. L’objet value est recréé.
  2. Chaque composant qui appelle useContext(DataContext) est restitué.
  3. Même le composant ThemeToggler est restitué, même si theme n’a pas changé !

Vous pouvez optimiser cela avec useMemo et diviser les contextes (NameContext, ThemeContext), mais cela conduit à “Context Hell” (Pyramid of Doom).

La solution 1 : Zustand (le remplacement du monolithe)

Zustand est à peu près “Redux sans le passe-partout”. Il utilise une architecture Store, mais résout le problème de re-rendu avec les Sélecteurs.

importer { créer } depuis 'zustand' ;

const useStore = créer ((ensemble) => ({
  ours: 0,
  poisson : 0,
  augmenterBears : () => set((state) => ({ ours : state.bears + 1 })),
  augmenterFish : () => set((state) => ({ fish: state.fish + 1 })),
}));

fonction BearCounter() {
  // SÉLECTEUR : ce composant observe UNIQUEMENT les "ours"
  const ours = useStore((state) => state.bears);
  return <h1>{ours} Ours</h1> ;
}

Si increaseFish() est appelé, BearCounter ne pas restituer. Zustand compare la valeur de retour du sélecteur (state.bears). S’il n’a pas changé, la mise à jour est ignorée.

Puissance du middleware

Zustand a principalement une taille de 1 Ko, mais un middleware puissant. Persist : enregistre automatiquement l’état dans « localStorage ». Immer : autorise la syntaxe mutable (state.bears++) dans les mises à jour.

importer { persist } depuis 'zustand/middleware' ;

const useCartStart = créer (
  persister(
    (ensemble) => ({
      éléments : [],
      addItem : (id) => set((state) => ({ items : [...state.items, id] })),
    }),
    { nom : 'cart-storage' } // Entrez localStorage
  )
);

Accéder aux composants externes

Surtout, les magasins Zustand sont accessibles en dehors de React (par exemple, dans les fonctions utilitaires ou les intercepteurs d’API).

// api.ts
importer { useAuthStore } depuis './authStore' ;

const token = useAuthStore.getState().token; // Accès sans hook
fetch('/api', { en-têtes : { Autorisation : jeton } });

La solution 2 : Jotai (l’approche atomique)

Jotai (inspiré de Recoil) adopte une approche différente. Au lieu d’un grand magasin, vous disposez de milliers de minuscules Atoms. L’État se construit de bas en haut.

importer { atome, useAtom } depuis 'jotai' ;

// Déclare les atomes
prix constAtom = atome(10);
const quantitéAtom = atome(2);

// Atome calculé (dérivé)
const totalAtom = atom((get) => get(priceAtom) * get(quantityAtom));

fonction Panier() {
  const [total] = useAtom(totalAtom);
  retourner <div>Total : {total}</div> ; 
}

Cas d’utilisation : applications hautement interactives telles qu’une feuille de calcul, un éditeur de diagramme ou un tableau de bord où les dépendances sont complexes. Si vous mettez à jour « priceAtom », seuls les composants écoutant « price » ou « total » sont restitués. C’est une précision chirurgicale.

État du serveur : gestion de l’asynchrone

Nous préconisons fortement TanStack Query. Il gère les parties difficiles de l’état asynchrone :

  • Stale-While-Revalidate : affiche les anciennes données lors de la récupération des nouvelles.
  • Focus Refetching : met à jour les données lorsque l’utilisateur revient à la fenêtre.
  • Déduplication : si 10 composants demandent un profil utilisateur, une seule demande réseau est effectuée.
const {données, isLoading} = useQuery({
  queryKey : ['utilisateur', identifiant],
  queryFn : () => récupérer l'utilisateur (id),
  staleTime : 1000 * 60, // Les données sont fraîches pendant 1 minute
});

Mélanger cela avec Zustand est courant. Zustand détient l’état « filtre ». React Query contient les données de « liste » dérivées de ce filtre.

Résumé : La matrice de décision

ExigenceRecommandationPourquoi?
Données APIRequête TanStackMise en cache, déduplication et états de chargement intégrés.
Interface utilisateur mondialeZusstandLes sélecteurs simples et petits empêchent les nouveaux rendus.
Graphique complexeJotaïLe suivi des dépendances atomiques est puissant.
État du formulaireFormulaire de crochet de réactionLes entrées non contrôlées fonctionnent mieux.

10. Signaux : l’avenir ? (Préact/Solide)

React restitue les composants. Les signaux (adoptés par Preact, Solid, Vue, Angular) mettent à jour le DOM directement. compte const = signal(0); <div>{count}</div> Lorsque count.value++ se produit, la fonction Component ne se réexécute PAS. Seul le nœud de texte dans les mises à jour DOM. Il s’agit de la complexité O(1). React explore cela avec “React Compiler” (Forget), mais les signaux sont une primitive fondamentalement plus efficace pour une réactivité plus fine. Nous surveillons cet espace de près. Pour les tableaux de bord performants (crypto trading), nous utilisons parfois Preact + Signals à la place de React.

11. État persistant (local d’abord)

Zustand « persister » est simple. Mais qu’en est-il des données valides hors ligne d’abord ? Nous nous dirigeons vers le Local-First Software (LoFi). Des outils comme RxDB ou Replicache. Le “State Manager” est en fait une base de données locale qui se synchronise en arrière-plan. Cela traite le serveur comme une source de vérité « secondaire ». Le client est principal. Cette architecture rend les applications instantanées (latence de 0 ms).

13. Le modèle d’observateur (MobX)

Avant Signals, il y avait MobX. Il utilise des objets « Observables » et des « Observateurs » (Composants). C’est comme de la magie. Vous modifiez un objet user.name = "John" et le composant est mis à jour. Nous ne recommandons pas MobX pour les nouveaux projets car il cache trop de complexité (Magic Proxies). Il est notoirement difficile de déboguer « Pourquoi ce rendu ? ». Cependant, pour les applications très spécifiques et riches en données (comme une feuille de calcul), MobX est plus rapide que Redux en raison de son suivi précis des dépendances.

14. Redux Toolkit (RTK) : la modernisation

Si vous devez utiliser Redux (Enterprise Legacy), utilisez RTK. Il se comporte comme Zustand.

  • Aucune instruction switch.
  • Immer intégré (syntaxe mutable).
  • Requête RTK intégrée (similaire à la requête TanStack). La migration de l’ancien Redux vers RTK réduit la taille du code de 60 %. Mais si vous repartez à zéro : Zustand fait 1 Ko, RTK fait 40 Ko. Choisissez judicieusement.

15. Honorer les morts : le recul

Il faut mentionner Recoil (Meta). Elle a inventé le concept « Atom » pour React. Mais il n’est actuellement pas maintenu (Meta déplacé vers d’autres outils internes). Jotai a repris le flambeau. Si vous disposez d’une base de code Recoil, migrez vers Jotai. L’API est similaire à 90 % (useRecoilState -> useAtom). Ne démarrez pas de nouveaux projets avec Recoil.

16. Conclusion

La gestion de l’État ne consiste plus à trouver « la bibliothèque unique pour les gouverner tous ». Il s’agit de composer des outils spécialisés. Chez Maison Code, nous utilisons par défaut la pile Zustand + React Query. Il est robuste, performant et convivial pour les développeurs.


Refactoriser Legacy Redux ?

Votre application est-elle enfouie sous des réducteurs passe-partout et lents ?

Embauchez nos architectes.