Cet article a pour but de vous présenter la mise en place de Redux au sein d’un projet Angular (2+).
Pour ce faire, j’ai eu l’occasion de découvrir la librairie ngrx, qui est l’alliage entre Angular, RxJS et Redux.
Présentation de Redux
Parlons de Redux en lui-même. Il s’agit d’abord d’une librairie Javascript structurante (on parle même de pattern Redux) qui a été initialement créée pour être couplée à React. Toutefois, il existe aussi des implémentations pour Angular et VueJS.
Redux nous permet de gérer l’état de notre application, c’est à dire toutes les données de notre application à un instant T.
Cet état (state) est stocké dans un objet Javascript appelé le store.
Lorsque l’on développe avec Angular, le principe est de créer des composants et de les imbriquer entre eux : soit directement dans le template HTML, soit à l’aide du routeur Angular. Cet ensemble de composants forme au final notre application.
Certains composants sont donc dépendants d’autres et peuvent ainsi s’échanger des données à l’aide du mécanisme de binding (et des décorateurs Input et Output).
Plus nous avons de composants, plus il devient compliqué de connaître l’impact lorsqu’une donnée est rafraîchie sur les autres composants. C’est notamment vrai lorsqu’on utilise le mode de binding bidirectionnel (2-way).
Redux propose un cadre permettant de gérer facilement le rafraîchissement des données et de limiter les effets de bord. Pour ce faire, 3 principes importants :
- Les données de notre application (state) sont contenues dans le store, qui devient dès lors la seule source de vérité
- Le state est global, et donc accessible à n’importe quel composant via le store
- Le state est en lecture seule. Un composant ne peut donc pas le modifier directement
Le pattern Redux
Tout changement d’état n’est possible que par l’intermédiaire d’actions et de reducers.
Une action peut être déclenchée par un composant (ou par autre chose, nous le verrons un peu plus bas dans cet article) dans le but de provoquer une mise à jour du state. Concrètement, une action est caractérisée par un nom et un payload si l’on veut transmettre des paramètres.
Pour mettre à jour le state, un reducer est déclenché afin de créer un nouvel état à partir du précédent, et d’autres paramètres si besoin.
Un reducer est une fonction pure : c’est à dire qu’elle retourne le même résultat pour les mêmes paramètres d’entrée et qu’elle n’a pas d’effet de bord. On peut donc commencer à parler de prédictibilité.
Tous ces éléments sont liés par un mécanisme de dispatcher et ne sont donc pas fortement couplés.
Une fois l’exécution du reducer terminée, le nouveau state est diffusé aux composants qui sont abonnés (grâce à RxJS et aux Observables).
Un exemple :
- Une action nommée SWITCH_THEME permet de basculer l’application en mode clair (light) ou foncé (dark)
- Un composant déclenche l’action SWITCH_THEME, ce qui déclenche aussitôt l’appel à un reducer
- Le reducer récupère le state courant depuis le store et en génère un nouveau avec l’autre thème (dark->light ou light->dark)
- Le composant ou service dédié à gérer le chargement du style CSS est averti et change le thème
Les side effects
La boucle UI -> Action -> Reducer -> Store est synchrone. Cela nous permet d’assurer qu’elle soit prédictible (puisque les reducers sont des fonctions pures) et rejouable. Toutefois, il se peut que certaines actions soient non-prédictibles, par exemple si elles nécessitent une communication avec l’extérieur (ex: HTTP, Websockets, SignalR…).
Comment traiter un comportement asynchrone dans une boucle synchrone ?
C’est là que rentrent en jeu les side effects.
Les side effects (ou « effets de bords ») sont des fonctions non pures qui vont être aussi déclenchées par des actions.
Une fois exécuté, ce dernier peut déclencher une autre action chargée de traiter le résultat à l’aide d’un reducer, permettant ainsi de revenir dans notre boucle initiale.
Un exemple :
- Une action LOAD_DATA est déclenchée au chargement de l’application
- Un side effect est déclenché. Il s’occupe de lancer la requête HTTP et pendant ce temps, l’action se termine*
- Quand la réponse du serveur est obtenue, une nouvelle action LOAD_DATA_SUCCESS est déclenchée**
- Le reducer traite la réponse et crée un nouveau state à l’aide du state précédent et des données reçues
* L’action LOAD_DATA peut aussi déclencher un reducer, par exemple, pour afficher un loader à l’utilisateur
** Le side effect peut aussi déclencher une autre action LOAD_DATA_FAILED si une erreur est survenue lors de la requête
On peut donc compléter notre première boucle avec une deuxième :
Redux DevTools
Redux DevTools est un outil d’aide au développement d’applications utilisant Redux. Il permet d’observer et de loguer les actions jouées dans le but de comprendre le fonctionnement de l’application mais aussi pour la debugger.
Il existe une extension pour Chrome et Firefox, mas il est également possible d’utiliser Redux DevTools pour Edge, ElectronJS ou encore iOS et Android…
Redux DevTools va lister chaque action exécutée dans le temps et montrer son impact (ou plutôt celui du reducer) sur le state.
Redux DevTools ne s’arrête pas là. Il permet également (grâce aux boutons Play, Suivant et Précédent) de rejouer une ou plusieurs action(s) ou même la totalité pour voir les impacts sur le state.
Il est également possible d’importer ou exporter un state (au format .json) pour l’exploiter et même rejouer un scénario complet.
Conclusion
Gérer de l’état de notre application est finalement ce que nous faisons à chaque fois que nous développons une application Web : récupérer des données, les mettre à jour, les rafraîchir etc.
Cependant, cette gestion va énormément dépendre des pratiques et des habitudes de l’équipe de développement, notamment en fonction de l’expérience de chacun, des frameworks/librairies utilisées, des architectures mises en place etc. Il y a donc N façons de le faire.
Redux apporte donc un cadre où la gestion de cet état va se faire à l’aide de classes/fonctions faiblement couplées et normalement prédictibles. Et grâce notamment à Redux DevTools, il devient aisé d’observer l’impact d’une action sur l’état de notre application et de le debugger.
2 Commentaires
Bonjour
Il est absurde d’utiliser Redux avec Angular
https://www.journaldunet.com/solutions/dsi/1487396-surtout-pas-de-redux-avec-angular/
Auteur
Bonjour, votre article cible la librairie Redux en particulier, or dans mon article que je parle de ngrx, qui est une adaptation de Redux pour Angular avec des Observables.
Toutefois, je suis prêt à échanger avec vous et à connaître vos arguments qui font qu’utiliser l’une de ces librairies pour un projet Web est « absurde ».