Redux - fromScarso2King - 1 - Il Data Flow
Una introduzione al data flow di Flux e Redux
Introduzione
Nella puntata zero abbiamo visto cosa è lo stato di un’ applicazione, cosa è la gestione di stato e quali sono i problemi generati dal non avere una gestione centralizzata dello stato. In questa puntata invece andremo più nel concreto e analizzeremo il pattern Flux e più nel dettaglio la libreria Redux.
Flux, Redux e chi più ne ha più ne Mettux
Flux è un pattern ideato da facebook(tanto per cambiare), che se applicato in maniera corretta permette una gestione dei dati particolarmente efficiente.
Gli elementi in gioco quando si parla di flux sono principalmente 3:
- Le view sono tipicamente componenti React, quindi la UI della nostra applicazione.
- Gli store sono i contenitori dei dati della nostra applicazione, l’insieme di essi è quindi il nostro stato applicativo.
- Il dispatcher per ultimo è lo strumento che collega view e store, tramite il dispatcher le view possono aggiornare lo stato.
Come funziona?
Il concetto più importante da capire quando ci si approccia a questo pattern è il flusso dei dati, che è unidirezionale! Nella documentazione viene descritto come un “ciclo unidirezionale”, questa immagine può chiarire il concetto:
Non mi vorrei dilungare troppo su Flux, dato che non andremo ad utilizzare una implementazione diretta di flux in questa serie, piuttosto utilizzeremo una libreria che eredità molto da questo pattern, chiamata Redux!
Potete leggere qui le differenze principali tra Flux e Redux, le cose che penso siano più interessanti da sapere sono:
- In Flux lo store è l’elemento intelligente*, in Redux invece l’elemento intelligente è il reducer
- In Redux esiste un unico store (e molti reducer), in Flux invece possono coesistere molteplici store.
- In Redux lo stato è immutabile
* Con intelligente intendiamo che contiene la maggior parte delle logiche e della complessità dell’applicazione
Redux
In quest’ultima parte di questa prima puntata introdurremo Redux, il resto della serie sarà completamente dedicato a questa libreria, andando ad analizzare tutti gli elementi principali al fine di utilizzarlo al meglio e costruire un’ architettura che possa essere la base per una applicazione robusta, scalabile e performante.
Redux è IL tool per la gestione dello stato su React, esistono altre librerie per la gestione dello stato nel mondo React, ma nessuna offre un supporto ed una community così vasta. A mio parere inoltre, nessun’altra libreria per ora garantisce la stessa versatilità.
É giusto comunque specificare che non sempre c’è bisogno di una strumento così complesso, vi consiglio di leggere questo articolo prima di scegliere di utilizzare Redux nel vostro applicativo: Forse non hai bisogno di redux.
I 3 principi di Redux
Il flusso di Redux è descritto in questa immagine:
E segue i seguenti 3 principi:
- Singolo store — Single source of truth
- Lo stato è immutabile — State is read-only
- I cambiamenti sono fatti con funzioni pure — Changes are made with pure functions
Single Source of truth
Il primo principio può essere compreso da questa immagine:
Lo Stato applicativo è salvato all’interno di un unico oggetto, a cui tutte le componenti fanno riferimento.
Il nostro stato nell’esempio della chat potrebbe essere il seguente:
{
messages: [
{
from: 'some user',
to: 'another user',
text: 'the message',
created_at: 'a timestamp',
read_at: 'a timestamp',
},
// ...
],
}
State is read-only
In Redux non si dovrebbe mai modificare direttamente lo stato, l’unico modo per aggiornare lo stato infatti è quello di emettere una action, che è un oggetto che descrive cosa è successo:
dispatch({
type: "ADD_MESSAGE",
payload: {
from: "Lucia", // tipicamente l'ID dell'utente
to: "Mauro", // tipicamente l'ID dell'utente
text: "Biretta stasera?",
},
});
L’oggetto all’interno della funzione dispatch è la nostra action, riprendendo l’esempio della chat questa action potrebbe descrivere l’inserimento di un nuovo messaggio.
Changes are made with pure functions
Per capire il terzo principio bisogna capire come funzionano i reducer.
I reducer sono delle funzioni pure, cioè delle funzioni che:
- Restituiscono sempre lo stesso valore dati gli stessi input
- Non hanno effetti collaterali
In particolare i reducer sono funzioni che prendono come parametri lo stato applicativo e la action e restituiscono il nuovo stato applicativo in funzione della action passata:
function addMessageCase(oldState, action) {
const newState = {
...oldState,
messages: [
...oldState.messages,
{ ...action.payload, created_at: Date.now() }
],
};
return newState;
}function reducer(oldState, action) {
// Scusa Uncle Bob
switch(action.type) {
case 'ADD_MESSAGE':
return addMessageCase(oldState, action);
// ...
default:
return oldState;
}
}
Notare che per semplicità abbiamo usato uno switch per riconoscere il tipo di action, ma ricordate che lo switch è il male!
Come si vede il reducer non modifica direttamente lo stato, ma piuttosto genera il prossimo stato a partire dal vecchio! Nell’esempio è stato creato un case per il tipo di action ADD_MESSAGE che semplicemente aggiunge il nuovo messaggio nella lista dei messaggi.
Flusso asincrono
Molto spesso ci troviamo nelle nostre applicazioni a richiamare dei servizi esterni per reperire i dati di cui abbiamo bisogno, ad esempio chiamare un API per reperire gli articoli in un blog, effettuare un pagamento e così via.
Tutte queste azioni avvengono in maniera asincrona rispetto al nostro flusso, cioè effettueremo una chiamata ed ad un certo punto riceveremo una risposta da queste API, come facciamo ad introdurre questa asincronicità all’interno del modello Redux?
la risposta a questa domanda è Middleware!
Il Middleware è uno strato aggiuntivo che verrà posto a monte dello store, il suo compito è quello di intercettare queste azioni che necessitano di una gestione asincrona, effettuare la chiamata all’API e, una volta ricevuta risposta, emettere una action che andrà a ad aggiornare lo stato applicativo come una qualsiasi normale action.
Esistono tantissimi middleware per redux, i più conosciuti sono Redux-Thunk e Redux-Saga, ci sono tantissime differenze tra i due approcci che approfondiremo nelle prossime puntate dove, SPOILER ALERT, opteremo per redux saga.
Continua la serie
La prossima puntata tratterà delle librerie must have per la gestione di uno stato in una applicazione React. La trovi qui!.
Qui sotto trovi i link alle altre puntate di questa serie
Fuori serie: 0. Le basi