REACT
REACT
INTRODUZIONE
React è una libreria JavaScript che nasce per diventare la soluzione per sviluppatori frontend e App mobile basate su
HTML5. React è una libreria dichiarativa basata su componenti.
Dichiarativa perchè React consente di sviluppare User Interface UI facilmente rendendo il codice più leggibile e facile
da debuggare, progettare interfacce per ogni stato della applicazione in modo che ad ogni cambio di stato React
aggiornerà solamente le parti della UI che dipendono da tali dati.
I componenti sono simili a tag html, possono essere creati componenti singoli o composti con una struttura albero,
per UI complesse. Le interazioni e la logica per i componenti sono implementate in JavaScript e questo ci consente
facilmente di passare ed accedere strutture dati complesse in vari punti della applicazione senza dover salvare
informazioni sul DOM.
I primi due tag caricano React. Il terzo carica il codice del componente.
Le versioni di sopra sono intese solo per ambienti di sviluppo, e non sono adatte per ambienti di produzione. Le
versioni minificate e ottimizzate di produzione di React sono:
Verificare che la CDN che stai usando abbia settato l’header HTTP Access-Control-Allow-Origin: *:
Next.js
Next.js è un framework popolare e leggero per le applicazioni statiche e renderizzate lato server realizzate con
React. Include soluzioni per il routing e l’applicazione degli stili e richiede l’utilizzo di Node.js come ambiente server.
Gatsby
Gatsby è il miglior modo di creare siti statici con React, consente di utilizzare componenti React, ma il suo output è
costituito interamente da codice HTML e CSS pre-renderizzato, in modo da garantire tempi di caricamento rapidi.
Il file package.json
La cartella di progetto include un set di package già installati, facenti parte di Node.js, contenuti all’interno della
directory node_modules.
Una parte dei package sono necessari per l’esecuzione dell’applicazione e affinché essa possa funzionare.
Vi sono poi altri package, che forniscono invece strumenti di supporto per la verifica del codice, l’avvio di un server
Web per il debug, l’esecuzione di test automatizzati, l’assemblaggio dei file e delle risorse per il rilascio in produzione
e altro ancora. Essi non sono quindi indispensabili a runtime, ma solo in fase di sviluppo.
Il file package.json: si tratta di un file presente nelle applicazioni e package scritte per Node.js, contiene un
oggetto JSON che riepiloga le informazioni salienti del package, come il nome (name) e la versione (version), le
dipendenze necessarie a runtime (dependencies) e quelle per il solo sviluppo (devDependencies), più una serie di
comandi che possono essere eseguiti attraverso npm, il package manager di Node.js, definiti nella proprietà scripts.
I comandi react-scripts
create-react-app include il package react-scripts che fornisce una serie script già pronti in grado di eseguire diverse
operazioni dal Prompt dei Comandi.
Aprendo il Promt e spostandosi nella cartella del progetto, è possibile lanciare gli script con
npm Comando.
I comandi disponibili sono:
- npm start esegue la transpilazione del codice sorgente dell’applicazione, mostrando eventuali errori. Al
termine del processo, viene attivato il server Web per il debugging e aperto il browser per visualizzare la
pagina iniziale dell’applicazione all’indirizzo localhost e alla porta predefinita (3000). Se il browser non
mostra il contenuto atteso, verificare gli eventuali errori nella scheda Console.
- npm build: esegue il build del progetto creando la directory omonima e generando al suo interno i file
assemblati dell’applicazione, ottimizzati per il rilascio in ambiente di produzione. I tool di bundling accorpano
e impacchettano i file necessari al funzionamento dell’applicazione, e avviano il processo di minification per
ridurre il peso dei file che il browser dovrà scaricare, massimizzando le performance.
- npm test: prepara la suite di test per l’esecuzione. Generalmente, il tool rimane attivo e in ascolto per
eseguire nuovamente i test a fronte di modifiche ai sorgenti dell’applicazione.
- npm eject: comando irreversibile che si esegue quando si vuole rinunciare al supporto di create-react-app. Il
comando rimuove dal progetto la dipendenza dal tool ed estrae i file di configurazione per Webpack, Babel e
gli altri tool a cui si appoggia, lasciandone il controllo completo nelle mani dello sviluppatore.
In React, gli elementi di un componente devono avere un solo elemento padre, ad esempio:
<div>
<h1> Hello World</h1>
<div>
<h2> Primo esempio<h2>
In questo caso, darebbe un errore. Se vogliamo scrivere il codice precedente, ritornando più elementi,
correttamente e senza inserire il secondo elemento nell’elemento padre div, o creare un altro div, è possibile
avvolgerli in <React.Fragment>...</React.Fragment> che consente di restituire elementi multipli come
risultato di un metodo render() senza dover creare un elemento DOM aggiuntivo per contenerli.
In pratica é possibile scrivere gli elementi dei componenti direttamente in Javascript, infatti, JSX è opzionale
e non richiesto per utilizzare React, in quanto esso verrá poi convertito in Javascript, ma è molto piú
comodo, semplice e leggibile. Il tool Babel converte, infatti, il codice JSX in JavaScript, in chiamate a
React.createElement, ad esempio:
const user = {
firstName: 'Giuseppe',
lastName: 'Verdi'
};
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
ReactDOM.render(element, document.getElementById('root'));
È possibile utilizzare JSX all’interno di istruzioni if e cicli for, assegnarlo a variabili, utilizzarlo come argomento di
una funzione e restituirlo come risultato di una funzione.
Non aggiungere le virgolette attorno alle parentesi graffe quando si include un’espressione JavaScript in un attributo.
Dovresti utilizzare o le virgolette (per le stringhe) o le parentesi graffe (per le espressioni), ma mai entrambe nello
stesso attributo.
React DOM utilizza la convenzione camelCase nell’assegnare il nome agli attributi, invece che quella utilizzata
normalmente nell’HTML, e modifica il nome di alcuni attributi, ad esempio, l’attributo class diventa className in
JSX e tabindex diventa tabIndex.
I componenti React implementano un metodo render() che riceve dati in input e ritorna cosa deve visualizzare.
file index.html
<div id="root"></div>
File html che contiene il tag root e rappresenta l’elemento padre del DOM in c0u65Zzzi gli verrà reindirizzata e
visualizzata tutta l’applicazione.
file Orologio.js
Questo file contiene il componente Orologio
import React from 'react';
function Orologio() {
const oraMillisecondi = Date.now();
const ora = new Date(oraMillisecondi);
return (
<p> {ora.toLocaleTimeString()}</p>
)
}
export default Orologio;
Viene importata la libreria React, dopodichè viene creato il componente Orologio attraverso una function. Con
Date.now() si ottiene l’ora attuale in millisecondi, dopo si crea un oggetto di tipo Date. Il componente ritorna un
elemento p in cui riceve l’oggetto ora e toLocaleTimeString() lo visualizza in formato ore/min/sec. Infine viene
esportato il componente.
file App.js
import React from 'react';
import Orologio from './Orologio';
function App() {
return (
<div className ="App">
<h1>Ora Attuale</h1>
<Orologio/>
</div>
)
}
export default App;
è il componente padre di tutta l’applicazione, importa tutti i componenti figli, in questo caso solo il componente
Orologio e ritorna la struttura di tutti i componenti dell’applicazione.
Vediamo che importato il componente Orologio, questo può essere inserito nell’applicazione attraverso ad un
semplice tag <Orologio/>.
I tag dei componenti devono iniziare sempre con lettera maiuscola a differenza dei tag JSX.
file index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
function orario() {
ReactDOM.render(<App/>, document.getElementById('root'));
}
setInterval(orario, 1000)
Data la function:
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p> Prima App React </p>
</header>
</div>
);
}
Importare import React, { Component } from 'react';
Si usa la keyword class, seguita dal nome, e si estende Component e si inserisce tutto il blocco di codice nel metodo
render() che viene chiamato ed eseguito automaticamente di default.
COMPONENTI E PROPS
I Componenti permettono di suddividere la UI in parti indipendenti e riutilizzabili . Concettualmente, i componenti
sono come funzioni JavaScript: accettano in input dati sotto il nome di “props” e ritornano elementi che descrivono
cosa dovrebbe apparire sullo schermo.
Props rappresenta un oggetto che contiene le proprietà di un componente.
Prendiamo l’esempio precedente dell’orologio e oltre all’ora attuale locale, inseriamo un altro componente orologio
che attraverso la props fusoorario mostra l’ora di 6 ore più avanti.
file App.js diventa:
function App() {
return (
<div className ="App">
<h1>Ora Attuale</h1>
<Orologio fusoorario = "0"/>
<Orologio fusoorario = "6"/>
</div>
)
}
Vediamo che la props fusoorario viene passato al componente come un attributo del tag.
function Orologio(props) {
const oraMillisecondi = Date.now() + props.fusoorario * 3600 * 1000;
const ora = new Date(oraMillisecondi);
return (
<p> {ora.toLocaleTimeString()}</p>
)
}
export default Orologio;
la function componente riceve come parametro props. All’ora attuale in millisecondi viene aggiunto il fusoorario
chiamato da props e convertito in millisecondi (* 3600 * 1000).
In un tag componente possono essere indicate quante props si vuole e richiamate poi nel componente (che riceve
come parametro sempre solo props) attraverso props.proprietà.
CLASSI E PROPS
Per richiamare ed utilizzare in una classe le proprietà props, prendiamo l’esempio dell’orologio convertito in classe:
file Orologio.js
import React from 'react';
class Orologio extends React.Component {
constructor(props) {
super(props);
}
render() {
const oraMillisecondi = Date.now() + this.props.fusoorario * 3600 * 1000;
const ora = new Date(oraMillisecondi);
return (
<p> {ora.toLocaleTimeString()}</p>
)
}
}
export default Orologio;
Possiamo estendere la classe sia con Component, importandolo in react, oppure semplicemente con
React.Component.
Per richiamare le props nella classe: si definisce il metodo constructor(), prima del metodo render(), ed in esso si
passa props, che si definisce poi nel metodo super().
Per utilizzare poi le props nella classe (nel metodo render()) si fa riferimento ad esse con:
this.props.proprietà
import React from 'react';
class Orologio extends React.Component {
constructor(props) {
super(props);
this.state = {
orario: new Date()
}
}
render() {
const oraMillisecondi = this.state.orario.getTime() + this.props.fusoorario * 3600 * 1000;
const ora = new Date(oraMillisecondi);
return (
<p> {ora.toLocaleTimeString()}</p>
)
}
}
export default Orologio;
Il metodo getTime() è un metodo dell’oggetto Date, uguale a now(), che ritorna l’ora attuale in millisecondi.
Questa classe viene reindirizzata con
ReactDOM.render(<App/>, document.getElementById('root'));
quindi per adesso ritorna l’ora in modo statico (non scorrono i secondi).
Un componente, nel suo ciclo di vita, esegue in ordine alcuni metodi. Questi metodi hanno lo scopo di gestire il ciclo
di vita di un componente React e cominciano con will quando precedono un evento e con did quando seguono un
evento.
Abbiamo accesso a questi metodi che potremo usare per eseguire determinate azioni quando un componente viene
creato, aggiornato o distrutto o, addirittura, decidere se un componente deve essere modificato in seguito alla
variazione di almeno una delle proprietà contenute nel suo oggetto State o alla ricezione di nuove Props.
Inizializzazione di un componente (MOUNTING)
- Il costruttore è il primo metodo che viene chiamato per una corretta inizializzazione, verranno assegnati i valori di
default alle props definite tramite l'oggetto defaultProps e verrà quindi inizializzato l'oggetto state.
- Il secondo metodo ad essere invocato è componentWillMount() che viene chiamato prima che il componente
venga reindirizzato (metodo render).
React consiglia di usare il costruttore al posto di questo metodo per ogni inizializzazione.
- render() è l'unico metodo obbligatorio quando si vuole definire un componente. Deve restituire almeno un React
Element oppure null o false, se non vogliamo mostrare nulla sullo schermo per quel componente. Deve essere una
funzione pura, non deve modificare direttamente l'oggetto props o state.
- ComponentDidMount() é il metodo che viene invocato dopo eseguito il render(). Questo è il metodo da usare per
manipolare il DOM o per recuperare eventuali dati da un server.
componentDidMount() {
this.time = setInterval(this.aggiornaTempo, 1000);
}
aggiornaTempo = () => {
this.setState( {
orario : new Date()
});
}
Per effettuare un Update dello state si usa il metodo setState() che riceve le proprietà dello state che devono essere
aggiornate. In questo caso abbiamo la proprietà orario, ma se ce ne fossero state altre nell’oggetto this.state queste
non verrebbero prese in esame.
setState() fa un confronto tra le proprietà dell’oggetto che riceve con quelle di this.state e se sono state modificate,
le sovrascrive. Nel nostro caso viene semplicemente ridefinita la proprietà orario con un nuovo Date() più recente,
che setState() confronta con quello in this.state e lo sovrascrive.
In questo modo l’orologio scatta ogni secondo.
Ricapitoliamo cosa succede e l’ordine con cui i metodi sono invocati:
1 Quando <Orologio/> viene passato a ReactDOM.render(), React invoca il costruttore del componente che
inizializza this.state con un oggetto che include l’ora corrente. In seguito viene aggiornato questo state.
2 React invoca il metodo render() del componente Orologio e apprende cosa dovrebbe essere visualizzato sullo
schermo. React si occupa di aggiornare il DOM in modo da farlo corrispondere all’output di render.
3 Quando l’output della renderizzazione di Orologio viene inserito nel DOM, React invoca il metodo
componentDidMount(). Al suo interno, viene impostato un timer che invoca aggiornaTempo una volta al secondo.
4 Ogni secondo, al suo interno, il componente Orologio implemente un aggiornamento della UI
invocando setState() con un oggetto che contiene la nuova ora corrente. Con la chiamata a setState(), React viene
informato del fatto che lo state è cambiato e invoca di nuovo il metodo render() per sapere che cosa deve essere
mostrato sullo schermo. Questa volta, this.state.orario nel metodo render() avrà un valore differente e l’output della
renderizzazione includerà l’orario aggiornato. React aggiorna il DOM di conseguenza.
componentWillUnmount() {
clearInterval(this.time);
}
GESTIONE EVENTI
Gli eventi React vengono dichiarati come per l’html, con la differenza che devono avere una sintassi:
camelCase = {this.metodo}
Prendiamo l’esempio dell’Orologio e inseriamo un bottone che stoppa e fa ripartire il tempo.
Bisogna gestire lo state del componente, e in esso inizializziamo una proprietà start:true:
this.state = {
orario: new Date(),
start: true
}
Dopodiche inseriamo nel return di render() un tag bottone:
<button>Stop</button>
Per gestire questo bottone, bisogna inserire un evento che chiami una function:
<button onClick={this.gestTime}>Stop</button>
Fin ora abbiamo implementato l’orologio in modo che una volta che il componente è montato nel DOM, parte
l’intervallo ogni secondo in componentDidMount() e lo smontiamo in componentWillUnmount().
In questo caso si deve gestire con l’evento e la function che chiama che se:
- l’orologio è stoppato, bisogna farlo ripartire;
- l’orologio NON è stoppato, bisogna stopparlo smontandolo.
Creiamo un metodo start(), con il setInterval(), e chiamiamo this.start() in componentDidMount().
Creiamo un metodo stop(), con clearInterval(), e chiamiamo this.stop() in componentWillUnmount().
Quando si definisce un componente classe, è comune usare un metodo della classe come gestore di eventi:
gestTime() {
this.setState((state) => {
state.start ? this.stop() : this.start();
return {start: !state.start}; //state contrario
})
}
setState() riceve una function, che riceve come parametro lo state precedent e ritorna lo state contrario. Prima
verifica che se state è start chiama il metodo stop() per fermare l’orologio, altrimenti chiama il metodo start() per far
ripartire l’orologio.
binding
Il metodo precedente, però, in questo modo da ERRORE, in quanto non è possibile leggere setState(). Questo perchè
in JavaScript, i metodi delle classi non sono associati di default. Nel metodo gestTime il this appartiene al metodo
stesso, ma non fa riferimento alla classe, quindi bisogna fare il binding, ovvero associare il metodo alla classe.
Il binding va fatto nel constructor() con:
this.metodo = this.metodo.bind(this)
nel nostro caso:
this.gestTime = this.gestTime.bind(this);
ma se abbiamo molti metodi, bisogna scrivere questo per ogni metodo nel constructor(). Un altro modo equivalente
sta nel dichiarare il metodo come un arrow function:
gestTime = () => {
this.setState((state) => {
state.start ? this.stop() : this.start();
return {start: !state.start};
})
}
Questo perchè, le arrow function non implementano il this, quindi in questo caso il this farà riferimento alla classe.
class Orologio extends React.Component {
constructor(props) {
super(props);
this.state = {
orario: new Date(),
start: true
}
}
render() {
const oraMillisecondi = this.state.orario.getTime() + this.props.fusoorario * 3600 * 1000;
const ora = new Date(oraMillisecondi);
return (
<p>In {this.props.paese} {ora.toLocaleTimeString()}
<button onClick = {this.gestTime}>{this.state.start ? 'STOP' : 'START'}</button>
</p>
)
}
aggiornaTempo = () => {
this.setState( {
orario : new Date()
});
}
gestTime = () => {
this.setState((state) => {
state.start ? this.stop() : this.start();
return {start: !state.start};
})
}
start(){
this.time = setInterval(this.aggiornaTempo, 1000);
}
stop(){
clearInterval(this.time);
}
componentDidMount() {
this.start();
}
componentWillUnmount() {
this.stop();
}
export default Orologio;
ARRAY DI ELEMENTI
Dato l’esempio di App.js:
. . .
<Orologio paese = 'Italia' fusoorario = '0'/>
<Orologio paese = 'Usa' fusoorario = '-6'/>
. . .
vediamo che contiene solo due elementi <Orologio>, ma può capitare che in un componente ci siano molti elementi
dello stesso tipo e per questo possono essere gestiti in array di elementi ed essere implementati dinamicamente.
Nella classe o al di fuori di essa dichiariamo una variabile array const e in essa andiamo a inserire tanti oggetti quanti
sono gli elementi, che conterranno le props degli elementi. Per il nostro esempio:
const clocks = [
{
paese: 'Italia',
fusoorario: 0
},
{
paese: 'Usa',
fusoorario: -6
}
]
Ora per prendere i dati dall’array e implementare dinamicamente gli elementi <Orologio>, creiamo nel componente
classe un metodo che lo gestisce e ritornerà tanti elementi <Orologio> quanti sono gli elementi oggetti.
getOrologi(){
return clocks.map((clock) => {
return <Orologio paese = {clock.paese} fusoorario = {clock.fusoorario}/>
})
}
Usiamo il metodo map() che esegue una function su ogni elemento che riceve dall’array che lo chiama (clocks).
Infine chiamiamo il metodo nel punto in cui devono essere elencati gli elementi, con:
{this.getOrologi()}
Iin questo modo, il metodo getOrologi() ritorna un errore, in quanto react, per ogni elemento di un array, si aspetta
un valore unico key. Tipo un ID che react usa per distinguere gli elementi, quindi o possiamo aggiungere un valore id
unico o usare un valore di cui siamo sicuri che sia unico per ogni elemento. Nel nostro esempio, possiamo usare la
proprietà paese come id:
getOrologi(){
return clocks.map((clock) => {
return <Orologio key = {clock.paese} paese = {clock.paese} fusoorario = {clock.fusoorario}/>
})
}
Il file App.js alla fine sarà:
import React from 'react';
import Orologio from './Orologio';
const clocks = [
{
paese: 'Italia',
fusoorario: 0
},
{
paese: 'Usa',
fusoorario: -6
}
]
class App extends React.Component {
getOrologi(){
return clocks.map((clock) => {
return <Orologio key = {clock.paese} paese = {clock.paese} fusoorario = {clock.fusoorario}/>
})
}
render() {
return (
<div className="App">
<h1>Ora Attuale</h1>
{this.getOrologi()}
</div>
)
}
}
export default App;
REACT ROUTER
React Router è una libreria che permette di creare applicazioni React con più pagine in cui la transizione fra una
pagina e l'altra avviene in maniera dinamica tramite Javascript, senza dover ogni volta ricaricare la pagina.
In React il Router non é altro che un componente che rotteizza altri componenti.
All'interno del componente Team, inseriamo un Link che porterà all'indirizzo localhost:3000/stagione e non verrà
ricaricata la pagina del browser.
NavLink è una versione particolare di Link. Esso può ricevere proprietá come activeClassName e activeStyle.
activeClassName=”nomeClasse” da uno stile quando un link é attivo.
Con activeStyle possiamo passare un oggetto attraverso il quale possiamo specificare lo stile che verrà applicato
all'elemento NavLink quando l'URL corrente è uguale al valore dell'attributo 'to' cioè quando location.pathname è
uguale al percorso specificato nell'attributo 'to'.
FUNZIONE PURA
Una funzione pura è una normale funzione con due caratteristiche:
1 Dato un insieme di input, la funzione deve sempre restituire lo stesso output.
2 Non produce effetti collaterali.
Per esempio, una funzione é quella che restituisce la somma di due numeri.
Le funzioni pure danno un output stimabile e sono deterministiche. Una funzione diventa impura quando esegue
qualcosa di diverso dal calcolare il suo valore di ritorno.
I TRE PRINCIPI
La complessità nella gestione dello stato di un’applicazione sta nella modificabilità e asincronicità. La soluzione di
Redux mira a rimuovere questi due aspetti, basandosi su tre principi fondamentali:
1 esiste una singola fonte di verità
lo stato dell’intera applicazione è memorizzato in un unico oggetto
2 lo stato è in sola lettura
non è possibile modificare direttamente le informazioni memorizzate nello stato dell’applicazione; solo
tramite azioni esplicite
3 le modifiche allo stato vanno fatte con funzioni pure
lo stato corrente viene sostituito da un nuovo stato generato da funzioni esclusivamente in base ad una
azione ed allo stato precedente
L’applicazione di questi principi definisce l’architettura di Redux e i vincoli che deve rispettare un’applicazione che
intende gestire lo stato secondo questo approccio.
VANTAGGI
Con Redux, le variazioni dello stato di un’applicazione risultano analizzabili e riproducibili, ed è più semplice
comprendere come si è arrivati ad una determinata situazione
Redux costringe ad organizzare il codice seguendo uno specifico pattern, il che definisce indirettamente uno
standard di codifica.
É possibile definire uno stato iniziale e far partire l’applicazione da quello stato, in questo modo da agevolare il
rendering di applicazioni JavaScript a partire dal server
É possibile tenere traccia delle transizioni di stato sia per il debugging che per implementare azioni.
INSTALLAZIONE
Se usiamo Node.js, possiamo installare Redux semplicemente con il seguente comando:
npm install --save redux
che scarica il pacchetto npm di Redux e lo rende disponibile per poter essere importato nella applicazione.
Se non utilizziamo un ambiente di sviluppo Node.js possiamo sempre la libreria scaricando la versione di
distribuzione del pacchetto npm ed inserendo un riferimento all’interno dalla pagina HTML della applicazione
<script src="redux.min.js"></script>
In questo caso Redux risulterà accessibile come una variabile globale window.Redux.
Quindi il flusso generale per modificare lo stato corrente di un’applicazione segue i seguenti passi:
1 viene individuata una Action, eventualmente generata tramite un Action creator
2 tale Action viene inviata allo Store tramite un Dispatch
3 all’interno dello Store, un Reducer sostituisce allo stato corrente l’eventuale nuovo stato individuato
in base all’analisi della Action
Store
Lo store è un grande oggetto JavaScript che rappresenta lo stato corrente dell'applicazione, e ogni volta che lo stato
si aggiorna, la vista è rivisualizzata.
L'intero stato dell'applicazione è all'interno dell'oggetto store.
Lo store ha tre metodi per comunicare con il resto dell'architettura. Sono:
Store.getState() per accedere all'albero dello stato corrente della applicazione.
Store.dispatch(action) per innescare un cambiamento dello stato basato su un'azione.
Store.subscribe(listener) per ascoltare qualsiasi cambiamento nello stato. Verrà chiamato ogni volta che
un'azione viene inviata.
index.js
import { createStore } from "redux";
Ora ascolteremo eventuali modifiche nello store e poi con console.log() lo stato corrente dello store.
store.subscribe( () => {
console.log("State has changed" + store.getState());
})
Action/Action Creators
Le action sono semplici oggetti JavaScript che inviano informazioni dalla applicazione allo store. Se si dispone di un
semplice contatore con un pulsante di incremento, premendolo risulterà un'azione che viene attivata, che assomiglia
a questo:
{
type: "INCREMENT",
payload: 1
}
Lo stato dello store si modifica solo in risposta a un'azione. Ogni azione deve avere una proprietà di tipo (type) che
descrive ciò che intende fare l'azione.
É consigliato mantenete la azione piccola in quanto rappresenta la quantità minima di informazioni necessarie per
trasformare lo stato dell'applicazione.
Nell'esempio precedente, la proprietà type è impostata su "INCREMENT", ed è inclusa una ulteriore proprietà
payload che potrebbe essere rinominata in qualcosa di più significativo.
Durante la scrittura di codice Redux, normalmente non si utilizzano le azioni direttamente, ma saranno chiamate le
funzioni che ritornano le azioni chiamate action creator.
L’action creator dell action precedente di incremento, sará:
Reducers
Il reducer (riduttore) utilizza le informazioni delle action per aggiornare effettivamente lo stato.
Un reducer dovrebbe essere una funzione pura. Dato un insieme di input, deve sempre restituire lo stesso output.
Oltre a ciò, non dovrebbe fare più nulla.
il reducer per il nostro contatore.
REDUX E REACT
Abbiamo visto che con Redux inviamo le azioni e recuperiamo il nuovo stato utilizzando store.dispatch() e
store.getState().
Adesso imparareremo a collegare uno store Redux con React, con la libreria react-redux e vedremo
come:
1 organizzare i componenti contenitore e i componenti presentazione
2 come connettere react e redux utilizzando connect()
3 come spedire le azioni utilizzando mapDispatchToProps
4 come recuperare lo stato utilizzando mapStateToProps
2) La libreria react-redux
Per collegare React a Redux sarà necessario installare una libreria supplementare chiamata react-redux.
npm install --save react-redux
La libreria esporta solo due API che è necessario ricordare, un componente <Provider/> e una funzione di
ordine superiore connect().
3) Il componente Provider
L'intera applicazione deve poter accedere allo store . Redux ha bisogno di rendere accessibile lo store dei
dati all'intero albero del componente react a partire dal componente principale. Il Provider consente di
passare i dati dall'alto verso il basso aggiungendo lo stato a tutti i componenti.
Avvolgiamo il provider intorno al componente dell'app e cosí i discendenti del componente hanno accesso
ai dati.
4) Il metodo connect()
Messo a disposizione lo store alla applicazione, abbiamo bisogno di collegare react con lo store. L'unico
modo in cui è possibile comunicare con lo store è inviando azioni e recuperando lo stato.
connect() permette appunto di collegare React con lo store, e con l'aiuto di due metodi:
mapDispatchToProps e mapStateToProps, di mappare e passare, solo le dispatch e la parte di store
interessati al componente dumb React come props.
const mapStateToProps = state => { const mapDispatchToProps = dispatch => {
return { return {
prop : state.prop dispatch : () => dispatch(actionCreator())
} }
} }
Vediamo ora un esempio concreto di un'applicazione Rubrica telefonica con le seguenti caratteristiche:
visualizzare tutti i contatti
aggiungere un nuovo contatto
rimuovere contatto
components: conterrá i componenti dumb di React, a cui non importa se si sta utilizzando Redux o meno.
containers: conterrá i componenti smart di React che invia azioni allo store di Redux. L'associazione tra
redux e react si svolgerà qui.
actions: conterrá gli action creators.
reducers: ogni reducer ha un singolo file, questa directory conterrá tutta la logica dei reducers.
store: conterrá la logica per l'inizializzazione dello stato e la configurazione dello store.
Questo modello di struttura é detto Rails e andrebbe bene applicazioni piccole e medie. Quando l’app cresce puo
essere piú comodo considerare il modello Dominio, dove ogni funzionalità avrà una directory per conto suo, e tutto
ciò che riguarda tale funzionalità sarà all'interno di essa.
const initialState = {
contactList: [],
newContact: {
name: '',
email: '',
tel: ''
}
}
Notiamo che esso riceve due parametri: il rootReducer e lo stato iniziale initialState (opzionale)
Importiamo initialState creato precedentemente.
I reducers creati, dovranno diventare le proprietà del combineReducers e per questo devono essere importati ed
inseriti con lo stesso nome della proprietà del pezzo di stato che riguardano
5)Creazione rootReducer
Questo sará il reducer principale che unirá in un unico reducer, tutti i reducer dell’applicazione.
Nella directory reducers, creiamo un file rootReducer.js, con combineReducers vuoto che unirá I reducers che
creeremo successivamente.
}
)
E una volta creato lo importiamo nel file index.js per passarlo al createStore.
dopodiche’ con
export default connect(mapStateToProps, null)(ContactsList)
Un elemento che ci permette di salvare all’interno del browser informazioni rilevanti per la navigazione del sito, sono
i cookies. Ma il problema più grande dei cookies e’ che, per ricordarsi delle informazioni salvate, il browser deve
scambiarsi messaggi di stato con il server ad ogni nuova pagina che l’utente visita; questa caratteristica, sopratutto
nei siti dove è possibile prenotare viaggi o abitazioni, si traduce in un grande spreco di risorse che potevano essere
utilizzate per velocizzare il tempo di risposta dei siti web.
Grazie all’avvento dell' HTML5 sono stati introdotti due oggetti che ci permettono di salvare i dati passati dall’utente
all’interno dei propri browser: sessionStorage e localStorage.
Entrambi condividono le stesse proprietà e funzioni, le differenze tra i due sono che: il sessionStorage perderà tutti i
dati quando la finestra del browser verrà chiusa.
Mentre tutto quello che salviamo all’interno di un oggetto localStorage, sarà mantenuto dentro al browser e
resisterà anche allo spegnimento del computer.
All’interno di questi oggetti, possiamo salvare solo delle stringhe di testo, proprio come per i cookie, ma andando ad
utilizzare localStorage, o sessionStorage se soddisfano le necessità, non sarà necessaria alcuna comunicazione con il
server dato che la pagina web potra’ interrogare direttamente il browser.
Vediamo in specifico localStorage, ma sara’ lo stesso per sessionStorage.
setItem() accetta due parametri. Il primo sarà l’indice e permetterà di richiamare successivamente il valore che
abbiamo impostato, il secondo è il valore vero e proprio che vogliamo salvare all’interno del browser.
Questi dati potranno essere poi visualizzati nell’ispeziona del browser
Vediamo che, l'action lanciata da store.dispatch() attraversa una serie di Middleware. Per far in modo che l'Action
venga inviata al Middleware successivo, o al Reducer nel caso dell'ultimo Middleware, bisogna invocare la funzione
next(action) che viene passata da Redux a ciascun Middleware.
In Redux sono particolarmente importanti perche rappresentano il punto dell'applicazione in cui e’ possibile fare
azioni asincrone come: chiamate API, timeout, ecc.
Una caratteristica importante, come si vede nell’immagine, e’ che possono essere inseriti piu’ middleware a catena
ed eseguiti nell’ordine un dopo l’altro.
Data una pila di middleware a catena, quando si effettua una dispatch, in pratica viene chiamato il primo
middleware. In genere un middleware verifichera’ se l’azione e’ di un type specifico che le interessa, simile a come
farebbe un reducer. Se e’ del type giusto, potrebbe eseguire una logica personalizzata, altrimenti passa l’action al
prossimo middleware nella pila.
A differenza di un reducer, un middleware puo’ avere effetti collaterali all’interno, includendo pause e altre logiche
asincrone.
Uso middleware
1)Definire la funzione middleware
Per usare i middleware in Redux, per prima cosa bisogna definire la funzione middleware, essa dovrà avere una precisa
segnatura.
// o in maniera equivalente
function mioMiddleware(store) {
return function (next) {
return function (action) {
// corpo della funzione Middleware
};
};
};
All'interno del middleware abbiamo accesso all'Action lanciata da store.dispatch(action). Possiamo usare la funzione
next(action) per far scorrere l'Action lungo la catena dei middleware, invochera’ il prossimo middleware, se non
viene invocata la funzione, l'Action non viene passata al prossimo middleware.
All'interno di ogni middleware abbiamo accesso anche ai metodi store.getState() per recuperare l'oggetto State
corrente e store.dispatch() che è la funzione dispatch() originale. Utilizzando questa funzione, potremo far
ripercorrere all'oggetto action tutta la catena dei middleware già attraversati prima del middleware corrente.
In questo caso specifico attendiamo l’esecuzione di eventuali middleware successivi prima di scrivere sulla console il
nuovo stato. Se non facessimo così otterremo lo stato corrente, invece del successivo.
Una volta definito il middleware lo rendiamo disponibile a Redux con
Questo farà sì che ogni transizione di stato verrà tracciata sulla console del browser:
Nel caso avessimo più middleware da eseguire prima del reducer, possiamo passarli come parametri della
funzione applyMiddleware() :
L’ordine di esecuzione dei middleware seguirà l’ordine con cui le funzioni vengono passate come parametro
ad applyMiddleware().
CHIAMATE ASINCRONE CON REACT REDUX
Vediamo ora come effettuare chiamate asincrone ad un server con React Redux.
Possiamo simulare un server locale con json-server, scaricabile da Npm, con i dati in json da servire. Una volta
installato, bisogna creare il file json da servire (db.json) e lanciare il server con:
json-server --watch db.json --port 3005 --nc
Adesso dobbiamo collegare la nostra app per leggere i dati da questo server.
Fin ora, abbiamo visto azioni sincrone dove l’oggetto payload con i dati, andava nello store, ma in questo caso i dati
si troveranno sul server e quindi bisogna fare una chiamata asincrona al server per prendere i dati.
Per gestire le chiamate asincrone, React utilizza i middleware, per facilitare il lavoro ci sono diversi package tra cui
redux-promise-middleware. Questo consente di gestire action creator asincroni, con la differenza che, in questo
caso, la payload riceve una promise:
const asyncAction = () => ({
type: ‘PROMISE’,
payload: new Promise(…)
})
Data una azione con un payload asincrono, il middleware trasforma l'azione in un'azione pending o un'azione
fulfilled/rejected, che rappresenta gli stati dell'azione asincrona.
Scaricato il package, nel index bisogna importarlo e passarlo allo store:
Per dialogare con il server, usiamo il fetch(), o altri package, che appunto permette una gestione delle chiamate
asincrone basata sulle promise
REACT HOOKS
Sono stati introdotti con React 16.8 e in pratica sono funzioni che consentono di inserire uno stato hai componenti di
tipo function, più semplici di quelli di tipo classe.
useState()
Per aggiungere uno stato, ad un componente di tipo function, si usa useState che prima deve essere importata da
React. useState è una funzione che viene definita con:
const [variabile, setVariabile] = useState(initialValue);
ovvero, essa ritorna una coppia di valori: il valore dello stato corrente ed una funzione che permette di
aggiornarlo(dispach).
È simile a this.setState di una classe.
L’unico parametro di useState è il suo stato iniziale.
useEffect()
Dice a React che il componente deve fare qualcosa dopo il rendering. React ricorderà la funzione passata e la
chiamerà più tardi dopo aver eseguito gli aggiornamenti DOM, ad ogni modifica.
Svolge gli stessi compiti di componentDidMount, componentDidUpdate, e componentWillUnmount .