Italian (Italiano) translation by Cinzia Sgariglia (you can also view the original English article)
Redux vi aiuta a gestire lo stato impostando lo stato a un livello globale. Nell'esercitazione precedente, abbiamo dato un buona occhiata all'architettura Redux e ai componenti integrali di Redux come azioni, creatori di azione, store e reducer.
In questo secondo post della serie, ci accingiamo a rafforzare la nostra comprensione di Redux e costruiremo in cima di ciò che già sappiamo. Inizieremo con la creazione di un'applicazione realistica Redux — un elenco di contatti — che è più complesso di un contatore di base. Questo vi aiuterà a rafforzare la vostra comprensione del singolo store e del concetto di reducer multipli che ho introdotto nell'esercitazione precedente. Poi più tardi parleremo dell'associazione del vostro stato Redux con un'applicazione di React e le procedure consigliate da tenere in considerazione durante la creazione di un progetto da zero.
Tuttavia, va bene se non avete letto il primo post — si dovrebbe ancora essere in grado di seguire, purché conosciate le basi di Redux. Il codice per il tutorial è disponibile nei repo, e può fungere da punto di partenza.
Creazione di un elenco di contatti utilizzando Redux
Costruiremo un elenco di contatti base con le seguenti caratteristiche:
- visualizzare tutti i contatti
- ricerca di contatti
- recuperare tutti i contatti dal server
- aggiungere un nuovo contatto
- inserire i nuovi dati del contatto sul server
Ecco a cosa somiglierà la nostra applicazione:


Coprire tutto in un tratto è difficile. Così in questo post ci concentreremo solo sulla parte Redux per aggiungere un nuovo contatto e visualizzare il contatto appena aggiunto. Da una prospettiva Redux, inizializzeremo lo stato, creeremo lo store, aggiungeremo reducer e azioni, ecc.
Nel prossimo tutorial, impareremo come collegare React e Redux e spediremo le azioni Redux da un front-end React. Nella parte finale, sposteremo l'attenzione verso le chiamate API usando Redux. Questo include il recupero dei contatti dal server e fare una richiesta al server durante l'aggiunta di nuovi contatti. A parte questo, creeremo anche una funzione barra di ricerca che vi permette di cercare tutti i contatti esistenti.
Creare uno schema dell'albero dello stato
Potete scaricare la demo dell'applicazione react-redux dal mio repository su GitHub. Clonate il repo e utilizzate il ramo v1 come punto di partenza. Il ramo v1 è molto simile al modello create-react-app. L'unica differenza è che ho aggiunto alcune directory vuote per organizzare Redux. Ecco la struttura della directory.
. ├── package.json ├── public ├── README.md ├── src │ ├── actions │ ├── App.js │ ├── components │ ├── containers │ ├── index.js │ ├── reducers │ └── store └── yarn.lock
In alternativa, è possibile creare un nuovo progetto da zero. In entrambi i casi, sarà necessario avere installato un boilerplate base di react e redux prima di iniziare.
È una buona idea avere uno schema dell'albero dello stato come prima cosa. A mio parere, questo vi farà risparmiare un sacco di tempo a lungo termine. Ecco uno schema del possibile albero dello stato.
const initialState = { contacts: { contactList: [], newContact: { name: '', surname: '', email: '', address: '', phone: '' }, ui: { //All the UI related state here. eg: hide/show modals, //toggle checkbox etc. } } }
Il nostro store deve avere due proprietà — contacts
e ui
. La proprietà contacts si prende cura di tutti i contatti relativi dello stato, considerando che ui
gestisce lo stato dell'interfaccia dell'utente specifico. Non esiste una regola rigida in Redux che vi impedisce di posizionare l'oggetto di ui
come un sotto-stato di contacts
. Sentitevi liberi di organizzare il vostro stato in modo che si senta significativo per l'applicazione.
La proprietà di contacts ha due proprietà nidificate all'interno di esso — contactlist
e newContact
. contactlist
è un array di contacts, mentre newContact
memorizza temporaneamente i dati di contatto mentre viene riempito il modulo di contatto. Ho intenzione di utilizzare questo come un punto di partenza per costruire la nostra fantastica app di elenco di contatti.
Come organizzare Redux
Redux non ha idea di come strutturerete la vostra applicazione. Ci sono alcuni modelli popolari, e in questo tutorial, parlerò brevemente di alcuni di loro. Ma dovreste scegliere un modello e attenervi a esso fino a quando non comprenderete appieno come tutti i pezzi sono collegati tra loro.
Il modello più comune che troverete è la struttura di file e cartella stile Rails. Avrete diverse directory di primo livello come quelle qui sotto:
- components: un posto per memorizzare i componenti dumb di React. A questi componenti non importa se si sta utilizzando Redux o meno.
- containers: una directory per i componenti smart di React che invia azioni allo store di Redux. L'associazione tra redux e react si svolgerà qui.
- actions: gli action creators andranno all'interno di questa directory.
- reducers: ogni reducer ottiene un singolo file, e metterete tutta la logica dei reducers in questa directory.
- store: la logica per l'inizializzazione dello stato e la configurazione dello store andrà qui.
L'immagine seguente illustra come potrebbe apparire la nostra applicazione se si segue questo schema:

Lo stile Rails dovrebbe funzionare per applicazioni piccole e medie. Tuttavia, quando la vostra app cresce, potete considerare di muovervi verso l'approccio stile dominio o altre alternative popolari che sono strettamente correlate al stile dominio. Qui, ogni funzionalità avrà una directory per conto suo, e tutto ciò che riguarda tale funzionalità (dominio) sarà all'interno di essa. L'immagine qui sotto confronta i due approcci, stile Rails a sinistra e stile dominio a destra.

Per ora, andate avanti e create le directory per components, containers, store, reducers e action. Cominciamo con store.
Store singolo, reducer multipli
Creiamo innanzitutto un prototipo per store e reducer. Dal nostro esempio precedente, ecco come apparirebbe il nostro store:
const store = createStore( reducer, { contacts: { contactlist: [], newContact: { } }, ui: { isContactFormHidden: true } }) const reducer = (state, action) => { switch(action.type) { case "HANDLE_INPUT_CHANGE": break; case "ADD_NEW_CONTACT": break; case "TOGGLE_CONTACT_FORM": break; } return state; }
L'istruzione switch ha tre casi che corrispondono alle tre azioni che creeremo. Ecco una breve spiegazione di cosa significhino le azioni.
-
HANDLE_INPUT_CHANGE
: Questa azione viene attivata quando l'utente inserisce nuovi valori nel modulo. -
ADD_NEW_CONTACT
: Questa azione viene inviata quando l'utente invia il modulo. -
TOGGLE_CONTACT_FORM
: Questa è un'azione dell'interfaccia utente che si occupa di mostrare/nascondere il modulo di contatto.
Sebbene questo semplice approccio funzioni, come l'applicazione cresce, l'utilizzo di questa tecnica avrà alcune lacune.
- Stiamo usando un singolo reducer. Anche se un singolo reducer va bene per ora, immaginate di avere tutta la logica del vostro business con un reducer molto grande.
- Il codice sopra non segue la struttura di Redux che abbiamo discusso nella sezione precedente.
Per risolvere il problema del singolo reducer, Redux ha un metodo denominato combineReducers che consente di creare più reducer e poi di unirli in una singola funzione. La funzione combineReducers migliora la leggibilità. Così ho intenzione di dividere il reducer in due — un contactsReducer
e un uiReducer
.
Nell'esempio precedente, createStore
accetta un secondo argomento opzionale che è lo stato iniziale. Tuttavia, se ci accingiamo a dividere i reducer, possiamo passare l'intero initialState
in una nuova posizione nel file, diciamo reducers/initialState.js. Poi importiamo un sottoinsieme di initialState
in ogni file di reducer.
Dividere reducer
Ristrutturiamo il nostro codice per risolvere entrambi i problemi. Innanzitutto, create un nuovo file denominato store/createStore.js e aggiungete il seguente codice:
import {createStore} from 'redux'; import rootReducer from '../reducers/'; /*Create a function called configureStore */ export default function configureStore() { return createStore(rootReducer); }
Successivamente, creare un reducer radice in reducers/index.js come segue:
import { combineReducers } from 'redux' import contactsReducer from './contactsReducer'; import uiReducer from './uiReducer'; const rootReducer =combineReducers({ contacts: contactsReducer, ui: uiReducer, }) export default rootReducer;
Infine, abbiamo bisogno di creare il codice per contactsReducer
e uiReducer
.
reducers/contactsReducer.js
import initialState from './initialState'; export default function contactReducer(state = initialState.contacts, action) { switch(action.type) { /* Add contacts to the state array */ case "ADD_CONTACT": { return { ...state, contactList: [...state.contactList, state.newContact] } } /* Handle input for the contact form. The payload (input changes) gets merged with the newContact object */ case "HANDLE_INPUT_CHANGE": { return { ...state, newContact: { ...state.newContact, ...action.payload } } } default: return state; } }
reducers/uiReducer.js
import initialState from './initialState'; export default function uiReducer(state = initialState.ui, action) { switch(action.type) { /* Show/hide the form */ case "TOGGLE_CONTACT_FORM": { return { ...state, isContactFormHidden: !state.isContactFormHidden } } default: return state; } }
Quando state creando i reducer, tenete sempre in mente quanto segue: un reducer deve avere un valore predefinito per il suo stato, e che deve sempre restituire qualcosa. Se il reducer non riesce a seguire questa specifica, si verificheranno degli errori.
Dal momento che abbiamo coperto un sacco di codice, diamo un'occhiata alle modifiche che abbiamo fatto con il nostro approccio:
- La chiamata
combineReducers
è stato introdotta per legare insieme i reducer divisi. - Lo stato di
ui
sarà affidato auiReducer
e lo stato dei contatti dacontactsReducer
. - Per mantenere i reducer puri, sono stati utilizzati operatori di diffusione. La sintassi dei tre punti è parte dell'operatore di diffusione. Se non siete a vostro agio con la sintassi di diffusione, è consigliabile utilizzare una libreria come Immutability.js.
- Il valore iniziale non è più specificato come argomento facoltativo di
createStore
. Invece, abbiamo creato un file separato per esso chiamato initialState.js. Stiamo importandoinitialState
e quindi impostando lo stato predefinito facendostate = initialState.ui
.
Inizializzazione dello stato
Ecco il codice per il file reducers/initialState.js.
const initialState = { contacts: { contactList: [], newContact: { name: '', surname: '', email: '', address: '', phone: '' }, }, ui: { isContactFormHidden: true } } export default initialState;
Azioni e creatori di azioni
Aggiungiamo un paio di azioni e creatori di azione per aggiungere le modifiche gestibili del modulo, aggiungete un nuovo contatto e attivazione/disattivazione dello stato dell'interfaccia utente. Se vi ricordate, i creatori di azione sono solo funzioni che restituiscono un'azione. Aggiungete il codice seguente in actions/index.js.
export const addContact =() => { return { type: "ADD_CONTACT", } } export const handleInputChange = (name, value) => { return { type: "HANDLE_INPUT_CHANGE", payload: { [name]: value} } } export const toggleContactForm = () => { return { type: "TOGGLE_CONTACT_FORM", } }
Ogni azione deve restituire una proprietà del tipo. Il tipo è come una chiave che determina quale reducer viene richiamato e come lo stato viene aggiornato in risposta a tale azione. Il payload è facoltativo, e si può effettivamente chiamare tutto quello che volete.
Nel nostro caso, abbiamo creato tre azioni.
TOGGLE_CONTACT_FORM
non ha bisogno di un payload perché ogni volta che l'azione viene attivata, il valore di ui.isContactFormHidden
viene attivato o disattivato. Le azioni con valori booleani non richiedono un payload.
L'azione HANDLE_INPUT_CHANGE
viene attivata quando cambia il valore del modulo. Quindi, per esempio, immaginate che l'utente stia riempiendo il campo email. L'azione quindi riceve "e-mail"
e "bob@example.com"
come input e il payload consegnato al reducer è un oggetto che assomiglia a questo:
{ email: "bob@example.com" }
Il reducer utilizza queste informazioni per aggiornare le proprietà pertinenti dello stato newContact
.
Inviare le azioni e ricevere notifiche dallo store
Il prossimo passo logico è quello di inviare le azioni. Una volta che le azioni sono inviate, lo stato cambia in risposta a questo. Per inviare le azioni e ottenere l'albero dello stato aggiornato, Redux offre determinate azioni di store. Sono:
-
dispatch(Action)
: Invia un'azione che potenzialmente potrebbe innescare un cambiamento di stato. -
getState()
: restituisce l'albero dello stato corrente della vostra applicazione. -
subscriber(listener)
: un listener di modifica che viene chiamato ogni volta che un'azione viene inviata e una parte dell'albero dello stato è cambiata.
Dirigetevi verso il file index.js e importate la funzione configureStore
e le tre azioni che abbiamo creato in precedenza:
import React from 'react'; import {render}from 'react-dom'; import App from './App'; /* Import Redux store and the actions */ import configureStore from './store/configureStore'; import {toggleContactForm, handleInputChange} from './actions';
Successivamente, create un oggetto store
e aggiungete un listener che registra l'albero dello stato ogni volta che un'azione viene inviata:
const store = configureStore(); //Note that subscribe() returns a function for unregistering the listener const unsubscribe = store.subscribe(() => console.log(store.getState()) )
Infine, inviate alcune azioni:
/* returns isContactFormHidden returns false */ store.dispatch(toggleContactForm()); /* returns isContactFormHidden returns false */ store.dispatch(toggleContactForm()); /* updates the state of contacts.newContact object */ store.dispatch(handleInputChange('email', 'manjunath@example.com')) unsubscribe;
Se tutto funziona bene, dovreste vedere questo nella console per gli sviluppatori.

Questo è tutto! Nella console per gli sviluppatori, potete vedere che lo store Redux viene registrato, così potete vedere come cambia dopo ogni azione.
Riepilogo
Abbiamo creato un'applicazione essenziale Redux per la nostra fantastica applicazione di elenco contatti. Abbiamo imparato a conoscere reducer, dividere i reducer per rendere la struttura della nostra app più pulita e scrivere azioni per mutare lo store.
Verso la fine del post, abbiamo sottoscritto lo store utilizzando il metodo store.subscribe()
. Tecnicamente, questo non è il modo migliore per fare le cose se avete intenzione di utilizzare React con Redux. Ci sono modi più ottimizzati per collegare il front-end di React con Redux. Ci occuperemo di quelli nel prossimo tutorial.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post