Quando ci si avvicina al mondo React, l’utilizzo di Redux o MobX come gestione dello stato è quasi automatico. O, comunque, cambiano le librerie, ma non l’architettura di base: si tratta sempre di qualcosa di simile al Redux Pattern con riduttori, azioni, selettori, middleware, ecc.
Ma esiste la possibilità di utilizzare un’architettura diversa? Qualcosa con RxJs come con Angular? Facendo qualche ricerca, sembra di sì. Vediamo più in dettaglio di cosa stiamo parlando.
Innanzitutto, dobbiamo pensare al di fuori del classico schema pull-based e passare a qualcosa di nuovo per chi proviene dal mondo React: un’architettura push-based.
Con le architetture data-push, i componenti della vista reagiscono semplicemente alle notifiche asincrone di modifica dei dati e rendono i valori correnti dei dati.
La libreria che ci permette di gestire lo store in questo modo è Akita:
“Akita è un pattern di gestione dello stato, costruito sopra RxJS, che prende l’idea di data store multipli da Flux e gli aggiornamenti immutabili da Redux, insieme al concetto di streaming dei dati, per creare il modello Observable Data Stores“.
In pratica, Akita ci permette di costruire facilmente soluzioni reattive, asincrone e di data-push per le nostre esigenze di gestione dello stato.
Un altro concetto importante da aggiungere è quello relativo alle Facades. Le facciate sono un modello di programmazione in cui viene fornita un’interfaccia pubblica più semplice per mascherare una composizione di usi interni di componenti più complessi.
Per costruire la nostra applicazione, ci affidiamo a RxJS e a React Hooks; non serve altro.
Consideriamo ora un esempio molto semplice costruito sulle idee trovate in alcuni articoli.
Nel nostro caso abbiamo bisogno di avere una lista di utenti e di poterci interfacciare attraverso le classiche funzioni CRUD.
Partendo dalla nota create-react-app con l’aggiunta di TypeScript, creiamo una cartella che conterrà le nostre entità; in questo caso, avrà solo una cartella “user” come figlio.
All’interno, definiamo una semplice interfaccia della nostra entità “utente” nel file model.ts:
Cominciamo ora a inizializzare il deposito della nostra entità, creando un’interfaccia “UsersState” e poi creando un deposito “UsersStore”, estendendo il deposito Akita, e infine esportandolo:
A questo punto, possiamo creare servizi per manipolare il deposito, basandoci anche sui metodi che un deposito Akita mette a disposizione.
È qui che possiamo utilizzare tutta la nostra conoscenza di RxJS per essere in grado di creare flussi più complessi per agire sul negozio.
Infine, attraverso la “QueryEntity”, possiamo prendere l’intero archivio – o solo una parte filtrata – e incanalarlo in un flusso osservabile di RxJS.
Infine, ma non meno importante, la creazione di un hook personalizzato che gestirà internamente tutti i problemi relativi a RxJS, Facades e Akita.
Per prima cosa, mappiamo ed esponiamo i servizi del nostro “userService”, in questo caso tutti. Poi, creiamo lo stato interno del nostro hook personalizzato. Infine, dobbiamo costruire i selettori per i cambiamenti di stato di\users\
e \active\
e gestire le sottoscrizioni con l’auto-cleanup.
Ora la nostra entità utente dovrebbe avere tutto ciò che serve. Importiamo i nostri hook personalizzati e il gioco è fatto.
Per giocare un po’, dividiamo l’applicazione in diversi componenti, in modo da testarla. Il risultato? Beh, funziona!
Ed ecco il componente figlio:
Ecco come l’applicazione viene eseguita nel browser:
Conclusioni
Sebbene questo esempio sia piuttosto semplice, i risultati sono piuttosto sorprendenti. È stato davvero facile – e anche abbastanza logico – collegare tutti i pezzi per comporre la gestione dello stato e, come abbiamo visto, non sono state necessarie configurazioni (di alcun tipo).
Per chi si avvicina per la prima volta a un’architettura come questa, la difficoltà maggiore è sicuramente rappresentata da RxJS. Per scrivere semplici servizi o query, può essere sufficiente conoscere le basi di RxJS; tuttavia, nel caso di applicazioni di grandi dimensioni con servizi complessi, una buona conoscenza della tecnologia fa un’enorme differenza (in positivo), dando davvero un vantaggio. Inoltre, è necessario prestare molta attenzione a dove e come si utilizzano le varie facciate nella propria applicazione. Essendo un pattern push, ogni cambiamento di stato innesca il ciclo di vita di React in ogni componente che utilizza i nostri hook; osservare e controllare le prestazioni è quindi molto importante.
Ovviamente, questo è solo l’inizio: c’è un mondo di cose da dire su Akita, RxJS, push-pattern ecc. e ci vorrebbe molto più di un semplice articolo per esplorarle tutte.
Lo scopo di questo contributo era quello di dare solo una piccola idea di questa “nuova” architettura per la gestione degli stati con React. Spero di aver centrato l’obiettivo.
Author: Mattia Ripamonti, UX/UI Engineer @Bitrock
Useful Resources:
1 – React Facade Best Practices