16/01/2023 7 Minutes read

Quid d’un state management dans vos applications web ?

Photo de Markus Spiske sur Unsplash

Le store, ou gestionnaire d’état en français, peut être assimilé à une base de données ou du cache côté frontend permettant de limiter la charge serveur.

Il est responsable de la gestion globale des données (et des états) à travers l’intégralité de l’application.

Il permet par exemple de stocker les informations relatives à l’utilisateur courant. Le fait de persister ces informations au fur et à mesure des interactions avec l’application permet d’y accéder à chaque étape du parcours et cela sans interroger le serveur.

En regroupant ces informations, que l’on pourrait assimiler à de la logique métier, au même endroit dans l’application, il est possible d’alléger les composants pour se limiter à l’aspect graphique notamment.

Le store en quatre points clés

  1. Une seule source de vérité, toutes les informations sont disponibles au même endroit.
  2. Le fait de regrouper ces informations dès la phase de conception permet l’uniformisation du modèle de données.
  3. La consommation des données à travers tous les composants est facilitée.
  4. Le principal inconvénient de cette approche est la complexification du code au premier abord.

Alternatives

Faisons tout d’abord un inventaire des solutions existantes, ce qu’il est possible de faire sans celui-ci.

Un simple objet

Il est possible de stocker nos données dans un objet accessible à toutes les composantes d’une application. Stocker ces données entre les différentes sessions utilisateurs pourra être envisagé.

globalThis

Aussi appelé window dans un navigateur, self dans un web worker, ou global en Node.js, globalThis a l’avantage d’être à tout moment accessible dans un contexte d’exécution. Il est donc possible d’y ajouter une propriété qui va contenir toutes les informations de notre application.

Le principal avantage de cette solution est que l’on utilise la plateforme fournie par JavaScript.

À l’inverse, cette solution n’est par défaut pas réactive, nous n’avons pas la possibilité de détecter lorsqu’une valeur a été mise à jour dans la structure de données stockée. Il est possible d’utiliser d’autres fonctionnalités de la plateforme pour contrer cet inconvénient, en utilisant des APIs telles que EventTarget ou Proxy.

localStorage et sessionStorage

L’inconvénient principal de globalThis est que les données stockées ne sont pas persistées lors d’un rechargement de page ou de la fin d’une session de navigation.

localStorage a pour but de faire persister des données qui survivront dans le temps. Il est possible d’attribuer une valeur dans le localStorage, que l’utilisateur ferme son navigateur, puis lorsqu’il reprend sa navigation, cette valeur peut être récupérée sans encombre.

sessionStorage ne survit qu’au rechargement de la page ou lorsqu’on change de page sur le même site ; si l’utilisatrice quitte son navigateur ou navigue sur un autre site, le sessionStorage est vidé.

L’inconvénient transverse à ces deux utilisateurs de l’API Web Storage est que l’on ne peut stocker que des données sérialisables, ce qui peut poser problème avec de grandes quantités de données.

Logique interne aux composants

Il est parfaitement possible d’utiliser un composant comme conteneur de l’information, les bibliothèques de composants fournissant la plupart du temps un moyen de garder l’état dans ou autour des composants.

En React, on peut utiliser le State pour l’état local ou Context pour garder un état plus global dans une application.

En Angular, on peut directement assigner une propriété à la classe d’un composant et celui-ci sera automatiquement mis à jour. On peut aussi utiliser le système d’injection de dépendances pour partager de la donnée entre plusieurs composants.

Arguments

Plusieurs contextes peuvent justifier l’utilisation d’une solution de gestionnaire d’état.

Lors du développement d’une application complexe, beaucoup de domaines métiers peuvent se croiser ; utilisatrice courante, permissions d’accès, périmètre de gestion, etc.

La structure de données peut être extrêmement complexe et naviguer à l’intérieur peut rendre la tâche aussi complexe que la structure et ainsi dégrader la maintenabilité de l’application (Voir Nested).

Enfin, dans une application standard, seulement l’utilisatrice modifie et consulte les données ; mais cela n’est pas tout le temps le cas. Dans le cadre d’applications de travail collaboratif comme Framapad, Google Docs ou Microsoft Word Online, l’état de l’application doit se mettre à jour à chaque interaction des différentes collaboratrices.

Décider

Deux critères essentiels doivent être considérés avant de sauter le pas.

L’équipe qui pratique

Le premier critère de décision devrait être l’équipe, Celles et ceux qui vont le subir en premier lieu. Certains l’auront déjà pratiqué, envie de sauter le pas ou au contraire des réticences (justifiées ou non). cf. Résistance au changement.

Comme cela a déjà été évoqué, cette approche implique une complexité accrue et nécessite donc l’adhésion de l’équipe. Une équipe de Juniors peu expérimentée par exemple aura davantage de mal à bien mettre en place le store dès le début du projet.

Le projet

Quel est l’objectif de l’application ? Est-ce que de nombreuses données vont y transiter ? Est-il envisageable d’enregistrer certaines informations dans le navigateur ?

Le projet est-il prévu pour durer dans le temps ? Est-ce notre équipe qui va suivre les évolutions de la plateforme ? Est-ce que l’application est suffisamment complexe pour passer du temps à implémenter un store et le maintenir ?

Conclusion

Si on essaie de porter ces critères à travers un exemple concret, on peut considérer qu’un site vitrine événementiel ne nécessite pas un gestionnaire d’état alors qu’une application qui dispose d’un espace client et qui vend des billets d’avion à travers un parcours complexe en plusieurs étapes pourrait être le bon cas d’usage.

L’offre

Sans rentrer dans le détail, voici quelques-unes des nombreuses solutions actuellement disponibles.

Redux

À l’origine de la plupart des alternatives actuelles, Redux reste un aujourd’hui une référence incontestable dans le domaine.

Des applications qui se comportent de manière cohérente, qui fonctionnent dans différents environnements (client, serveur et natif) et qui sont faciles à tester.
Offre une excellente expérience de développement, comme l’édition de code en direct combinée à un débogueur à déplacement temporel.

store.dispatch({ type: ‘counter/incremented’ })
store.dispatch({ type: ‘counter/decremented’ })
Source : Redux Fundamentals, Part 2: Concepts and Data Flow

Vuex

Il sert de gestionnaire d’état centralisé pour tous les composants d’une application, avec des règles garantissant que l’état ne peut être modifié que de manière prévisible.
Il y a de fortes chances que vous ayez rencontré des situations qui vous ont fait réfléchir à la manière de mieux gérer l’état en dehors de vos composants Vue, et Vuex sera la prochaine étape naturelle pour vous.

const store = createStore({ 
state () {…},
mutations: {…}
});
Source : What is Vuex?

MobX

Rend la gestion de l’état simple et évolutive en appliquant de manière transparente la programmation fonctionnelle réactive.

class Timer {
secondsPassed = 0
constructor() { makeAutoObservable(this) }
increase() { this.secondsPassed += 1 }
reset() { this.secondsPassed = 0 }
}
Source : Documentation MobX

NGXS

S’inspire du modèle CQRS mis en œuvre dans des bibliothèques telles que Redux et NgRx, mais réduit le nombre d’éléments inutiles en utilisant des fonctionnalités TypeScript modernes telles que les classes et les décorateurs.

export class ZooComponent {
constructor(private store: Store) {}
addAnimal(name: string) {
this.store.dispatch(new AddAnimal(name)).subscribe(() => this.form.reset());
}
}
Source : Introduction NGXS

Nano Stores

Un petit gestionnaire d’état pour React, React Native, Preact, Vue, Svelte et vanilla JS.
Il utilise de nombreux stores atomiques et la manipulation directe.

export const users = atom<User[]>([]);
export function addUser(user: User) {
users.set([…users.get(), user]);
}

Arbitrer

Critères de comparaison

Comment comparer les différents outils qui permettent de mettre en place un store dans notre application ?

Une attention toute particulière devra être apportée à la complexité. Une prise en main complexe pourrait décourager une équipe novice et par conséquent créer de la frustration.

Le poids de la communauté est une variable à prendre en compte. Cela permettra d’obtenir une aide si on rencontre des points de blocage ou de pouvoir s’inspirer d’articles pour améliorer l’implémentation de l’outil dans son application. Pour savoir si une communauté est forte ou non il y a divers points que l’on peut vérifier:

  • Le nombre d’étoiles Github permet de voir si une librairie est suivie. Une librairie à succès a potentiellement plus de chances d’être maintenue.
  • Le nombre de questions Stack Overflow est aussi un bon critère. Cela permet de différencier une librairie qui obtient des étoiles par effet de mode d’une qui obtient des étoiles de par son utilisation.
  • Le nombre de npm install peut aussi être une variable intéressante à prendre en compte.

Un point qui peut sembler évident est la compatibilité de l’outil. On évitera, par exemple, de mettre en place NgRX dans un écosystème React ou même l’utilisation d’une solution transverse pour les projets utilisant plusieurs framework.

Il faudra aussi faire attention à l’ancienneté de l’outil. Plus une librairie est ancienne, plus elle a de chance d’avoir connu des montées de version, des corrections et des évolutions. Pour une application business, on cherchera généralement la sécurité en allant vers des librairies qui ont déjà fait leurs preuves.

Un dernier point de comparaison peut être de regarder qui utilise l’outil. En général, si une grande société a choisi un outil en particulier, c’est qu’il y a des raisons. Il pourrait être intéressant d’examiner ces raisons et de voir si vous répondez aux mêmes critères.

Pondérer

Il n’est pas évident de pondérer son choix. De manière générale, il faudra chercher au niveau de son équipe. C’est l’équipe qui sera amenée à travailler avec le store, c’est donc à elle d’exprimer son souhait en toute connaissance de cause. Il faut que l’équipe comprenne ce qu’est un store. Il n’est pas favorable de forcer son équipe à travailler avec un outil qu’elle rejette.

Il faudra également prêter attention au niveau d’expérience de l’équipe en la matière. Parfois, il n’est pas conseillé à une équipe novice de mettre en place un store. Cela peut être complexe à prendre en main et peut générer plus de problèmes qu’autre chose.

Il est également important d’avoir un référent sur le sujet pour s’assurer que le store est bien implémenté et bien utilisé.

On peut aussi se demander quels problèmes un gestionnaire d’état peut résoudre. On notera qu’il améliore l’immuabilité de la donnée, qu’il facilitera potentiellement le débogage ou encore, qu’il permettra de faire transiter plus facilement la donnée. Si votre framework ne permet pas de faire cela facilement, il vous faudra alors envisager de mettre en place un store capable de répondre à ces manquements.

Vous devriez maintenant avoir une idée plus claire du sujet.
Il est cependant possible de creuser plus profondément chacune des solutions évoquées, ce qui fera l’objet d’autres articles plus spécifiques.

À ce propos, utilisez-vous déjà l’une de ces solutions et êtes-vous intéressé par un article sur un sujet particulier ? On attend avec impatience votre avis dans les commentaires.

Co-auteurs : Pierre LE ROUX, Lucie LENGLET, Yoann GUESNEROT


Quid d’un state management dans vos applications web ? was originally published in ekino-france on Medium, where people are continuing the conversation by highlighting and responding to this story.