Noțiuni de bază cu redux Aflați prin exemplu

Redux vă ajută să gestionați starea prin setarea stării la nivel global. În tutorialul anterior, am avut o privire bună asupra arhitecturii Redux și a componentelor integrale ale Redux, cum ar fi acțiunile, creatorii de acțiuni, magazinul și reductorii. 

În acest al doilea post al seriei, vom consolida înțelegerea noastră despre Redux și vom construi pe lângă ceea ce știm deja. Vom începe prin crearea unei aplicații realiste Redux - o listă de contacte - care este mai complexă decât un contor de bază. Acest lucru vă va ajuta să vă consolidați înțelegerea conceptului de magazin unic și a mai multor reductori pe care l-am introdus în tutorialul anterior. Mai târziu, vom vorbi despre legarea stării Redux cu o aplicație React și despre cele mai bune practici pe care ar trebui să le luați în considerare în momentul creării unui proiect de la zero. 

Cu toate acestea, este bine dacă nu ați citit primul post - ar trebui să puteți continua de-a lungul timpului, în timp ce știți elementele de bază Redux. Codul tutorialului este disponibil în repo și îl puteți utiliza ca punct de plecare. 

Crearea unei liste de contacte utilizând Redux

Vom construi o listă de contacte de bază cu următoarele caracteristici:

  • afișați toate contactele
  • căutați contacte
  • preluați toate contactele de pe server
  • adăugați un nou contact
  • împingeți datele noi de contact pe server

Iată cum va arăta aplicația noastră:

Produs final - Vizualizare listă de contacte


Produs final - Adăugați vizualizarea de contact

Acoperirea totul într-o singură întindere este greu. Deci, în acest post ne vom concentra doar pe partea Redux a adăugării unui nou contact și afișarea contactului nou adăugat. Din perspectiva Redux, vom inițializa statul, creând magazinul, adăugând reductori și acțiuni etc. 

În tutorialul următor, vom învăța cum să conectăm React și Redux și să trimitem acțiunile Redux de la un front-front React. În ultima parte, ne vom îndrepta atenția spre efectuarea apelurilor API utilizând Redux. Aceasta include preluarea contactelor de pe server și efectuarea unei solicitări de server în timp ce se adaugă contacte noi. În afară de aceasta, vom crea, de asemenea, o funcție de bara de căutare care vă permite să căutați toate persoanele de contact existente. 

Creați o schiță a arborelui de stat

Puteți descărca aplicația demo react-redux din depozitul meu GitHub. Clonați repo și utilizați v1 ramură ca punct de plecare. v1 ramura este foarte asemănătoare cu șablonul de creație-reacție-aplicație. Singura diferență este că am adăugat câteva directoare goale pentru a organiza Redux. Iată structura directorului.

. ├── package.json ├── publice ├── README.md ├── src │ ├── acțiuni │ │ ├── App.js ├── componente │ ├── containere │ │ ├── index.js ├────────────────────────────────────────────────┘ 

Alternativ, puteți crea un nou proiect de la zero. În orice caz, va trebui să aveți instalat un boilerplate de bază de reacție și un redux înainte de a începe. 

Este o idee bună să aveți mai întâi o schiță grosieră a arborelui de stat. În opinia mea, acest lucru vă va economisi mult timp pe termen lung. Iată o schiță grosieră a arborelui de stat posibil. 

con initialState = contacte: contactList: [], newContact: nume: ", nume:", email: ", adresa:", phone: ", ui: ascunde / afișează modalitățile, / / ​​bifați caseta de selectare etc.  

Magazinul nostru trebuie să aibă două proprietăți-contacte și ui. Proprietatea de contacte are grijă de toate stările legate de contacte, în timp ce ui gestionează starea specifică UI. Nu există o regulă greșită în Redux care să vă împiedice să plasați ui obiect ca un sub-stat al contacte. Simțiți-vă libertatea de a vă organiza statul într-un mod care să se potrivească cu cererea dumneavoastră. 

Proprietatea de contacte are două proprietăți imbricate în interiorul acesteia-listă de contacte și contact noulistă de contacte este o serie de contacte, în timp ce contact nou stochează temporar datele de contact în timp ce se completează formularul de contact. Voi folosi acest lucru ca punct de plecare pentru construirea aplicației noastre minunate de pe lista de contacte. 

Cum de a organiza Redux

Redux nu are o opinie despre modul în care vă structurați cererea. Există câteva modele populare acolo și în acest tutorial voi vorbi pe scurt despre unele dintre ele. Dar ar trebui să alegeți un model și să vă lipiți de el până când veți înțelege pe deplin modul în care toate piesele sunt conectate împreună.

Modelul cel mai obișnuit pe care îl veți găsi este structura fișierului și a folderului în stilul Rails. Veți avea mai multe directoare de nivel superior, cum ar fi cele de mai jos:

  • componente: Un loc pentru a stoca componentele React prost. Aceste componente nu le pasă dacă utilizați Redux sau nu.
  • containere: Un director pentru componentele React inteligente care expediază acțiuni la magazinul Redux. Legarea între redux și reacție va avea loc aici. 
  • acţiuni: Creatorii acțiunilor vor intra în acest director. 
  • reductoare: Fiecare reductor obține un fișier individual și veți plasa toată logica reductorului în acest director.
  • magazin: Logica pentru inițializarea stării și configurarea magazinului va merge aici. 

Imaginea de mai jos demonstrează modul în care poate arăta aplicația noastră dacă urmăm acest model:

Stilul Rails ar trebui să funcționeze pentru aplicații mici și mijlocii. Cu toate acestea, atunci când aplicația dvs. crește, puteți lua în considerare trecerea la abordarea în stil de domeniu sau la alte alternative populare care sunt strâns legate de stilul de domeniu. Aici, fiecare caracteristică va avea un director propriu, și tot ceea ce se referă la acea caracteristică (domeniu) va fi în interiorul său. Imaginea de mai jos compară cele două abordări, stilul Rails în stânga și stilul de domeniu în partea dreaptă. 

Pentru moment, continuați și creați directoare pentru componente, containere, magazin, reductoare, și acțiune. Să începem cu magazinul. 

Magazin unic, reduceri multiple

Să creăm un prototip pentru magazin si reductor primul. Din exemplul nostru precedent, acesta este modul în care arată magazinul nostru: 

magazin const = CreateStore (reductor, contact: ContactList: [], newContact: , ui: isContactFormHidden: true) = const reductorului (stare, acțiune) => comutator (action.type) caz "HANDLE_INPUT_CHANGE": pauză; cazul "ADD_NEW_CONTACT": pauză; cazul "TOGGLE_CONTACT_FORM": pauză;  stare de returnare; 

Instrumentul de comutare are trei cazuri care corespund celor trei acțiuni pe care le vom crea. Iată o scurtă explicație a acțiunilor. 

  • HANDLE_INPUT_CHANGE: Această acțiune este declanșată atunci când utilizatorul introduce noi valori în formularul de contact.
  • ADĂUGAȚI UN CONTACT NOU: Această acțiune este expediată când utilizatorul trimite formularul.
  • TOGGLE_CONTACT_FORM: Aceasta este o acțiune UI care se ocupă de afișarea / ascunderea formularului de contact. 

Deși această abordare naivă funcționează, pe măsură ce aplicația crește, folosirea acestei tehnici va avea câteva deficiențe.

  1. Folosim un singur reductor. Deși un singur reductor sună bine acum, imaginați-vă că aveți toată logica dvs. de afaceri sub un reductor foarte mare.  
  2. Codul de mai sus nu respectă structura Redux pe care le-am discutat în secțiunea anterioară.

Pentru a rezolva problema cu un singur reductor, Redux are o metodă numită combineReducers care vă permite să creați mai multe reductoare și apoi să le combinați într-o singură funcție de reducere. Funcția CombineReduceri îmbunătățește lizibilitatea. Așa că am să împart reductorul în două contactsReducer și a uiReducer

În exemplul de mai sus, CreateStore acceptă o facultativ al doilea argument care este starea inițială. Cu toate acestea, dacă vom împărți reductorii, putem muta întregul stare initiala la o nouă locație de fișier, să zicem reductoare / initialState.js. Apoi vom importa un subset de stare initiala în fiecare fișier de reducere. 

Împărțirea reductorului 

Să restructurăm codul nostru pentru a rezolva ambele probleme. Mai întâi, creați un fișier nou numit magazin / createStore.js și adăugați următorul cod:

import createStore de la "redux"; import rădăcinăReducer de la "... / reduceri /"; / * Creați o funcție numită configureStore * / export funcția implicită configureStore () return createStore (rootReducer);  

Apoi, creați un reductor rădăcină în reductoare / index.js după cum urmează:

import combineReducers din contactele de import "redux" de importReducer din './contactsReducer'; import uiReducer de la "./uiReducer"; const rootReducer = combineReduceri (contacte: contacteReducer, ui: uiReducer,) exportul implicit rootReducer;

În cele din urmă, trebuie să creați codul pentru contactsReducer și uiReducer.

reductoare / contactsReducer.js

importul inițial din "./initialState"; implicit la export contactReducer funcție (stare = initialState.contacts, acțiune) comutator (action.type) / * Adăugați contacte la matrice de stat * / caz "ADD_CONTACT": întoarcere ... de stat, ContactList: [... state.contactList, state.newContact] / * Introduceți mâna pentru formularul de contact. Sarcina utilă (modificări de intrare) devine fuzionat cu newContact obiect * / caz "HANDLE_INPUT_CHANGE": întoarcere ... de stat, newContact: ... state.newContact, ... action.payload implicit: stare return; 

reductoare / uiReducer.js

importul inițial din "./initialState"; uiReducer funcția implicită de export (stare = initialState.ui, acțiune) comutator (action.type) / * Arată / ascunde formularul * / caz "TOGGLE_CONTACT_FORM": întoarcere ... de stat, isContactFormHidden:! state.isContactFormHidden implicit : starea de returnare;  

Când creați reductori, țineți întotdeauna în minte următoarele: un reductor trebuie să aibă o valoare implicită pentru starea sa și trebuie întotdeauna să returneze ceva. Dacă reductorul nu respectă această specificație, veți primi erori.

Din moment ce am acoperit o mulțime de cod, să aruncăm o privire la schimbările pe care le-am făcut cu abordarea noastră:

  1. combineReducers a fost introdus un apel pentru a lega împreună reductorii împărțiți.
  2. Starea ui obiect va fi manipulat de către uiReducer și starea contactelor de către contactsReducer
  3. Pentru a menține reductorii, au fost utilizați operatori cu răspândire pură. Sintaxa cu trei puncte face parte din operatorul de răspândire. Dacă nu sunteți convins de sintaxa de răspândire, vă recomandăm să utilizați o bibliotecă ca Immutability.js.
  4. Valoarea inițială nu mai este specificată ca un argument opțional pentru CreateStore. În schimb, am creat un fișier separat pentru acesta numit initialState.js. Importăm stare initiala și apoi setarea stării implicite făcând state = initialState.ui

Inițializarea statului

Iată codul pentru reductoare / initialState.js fişier.

Const initialState = contact: ContactList: [], newContact: name: "nume:", e-mail: "adresa:", telefon: ",, ui: isContactFormHidden: true implicit la export initialState;

Acțiuni și creatori de acțiuni

Să adăugăm câteva acțiuni și creatori de acțiuni pentru a adăuga modificări ale formularului de manevră, adăugarea unui nou contact și schimbarea stării UI. Dacă vă amintiți, creatorii de acțiuni sunt doar funcții care returnează o acțiune. Adăugați următorul cod în acțiuni / index.js.

export const constContact = () => return type: "ADD_CONTACT", export const handleInputChange = (nume, valoare) => return type: HANDLE_INPUT_CHANGE export const toggleContactForm = () => return tip: "TOGGLE_CONTACT_FORM",

Fiecare acțiune trebuie să returneze o proprietate de tip. Tipul este ca o cheie care determină ce reductor este invocat și cum se actualizează statul ca răspuns la acea acțiune. Sarcina utilă este opțională și, de fapt, o puteți numi orice doriți. 

În cazul nostru, am creat trei acțiuni.

TOGGLE_CONTACT_FORM nu are nevoie de o sarcină utilă, deoarece de fiecare dată când acțiunea este declanșată, valoarea lui ui.isContactFormHidden se răstoarnă. Acțiunile cu valoare booleană nu necesită o sarcină utilă. 

HANDLE_INPUT_CHANGE acțiunea este declanșată atunci când se modifică valoarea formei. De exemplu, imaginați-vă că utilizatorul completează câmpul de e-mail. Acțiunea primește apoi "e-mail" și "[email protected]" ca intrări, iar sarcina utilă transmisă reductorului este un obiect care arată astfel:

email: "[email protected]"

Reductorul folosește aceste informații pentru a actualiza proprietățile relevante ale contact nou stat. 

Dispecerizarea acțiunilor și abonarea la magazin

Următorul pas logic este de a trimite acțiunile. Odată ce acțiunile sunt expediate, statul se schimbă ca răspuns la aceasta. Pentru a trimite acțiuni și pentru a obține arborele de stare actualizat, Redux oferă anumite acțiuni de stocare. Sunt:

  • expediere (acțiune): Trimite o acțiune care ar putea declanșa o schimbare de stat. 
  • getState (): Returnează arborele de stare curent al aplicației.
  • abonat (ascultător): Un ascultător de schimbare care este apelat de fiecare dată când o acțiune este expediată și o parte a arborelui de stat este schimbată. 

Deplasați-vă la index.js fișier și importați configureStore funcția și cele trei acțiuni pe care le-am creat mai devreme:

import Reacționează de la "reacționează"; import render de la "react-dom"; aplicația de import de la "./App"; / * Importul magazinului Redux și acțiunile * / import configurestore din './store/configureStore'; import toggleContactForm, handleInputChange din "./actions";

Apoi, creați un magazin obiect și adăugați un ascultător care înregistrează arborele de stat de fiecare dată când o acțiune este expediată:

const store = configureStore (); // Rețineți că subscribe () returnează o funcție pentru a anula înregistrarea ascultătorului const unsubscribe = store.subscribe (() => console.log (store.getState ()))

În cele din urmă, trimiteți câteva acțiuni:

/ * întoarce isContactFormHidden returnează false * / store.dispatch (toggleContactForm ()); / * întoarce isContactFormHidden returnează false * / store.dispatch (toggleContactForm ()); / * actualizează starea contact.newContact obiect * / store.dispatch (handleInputChange ('email', '[email protected]')) dezabonare;

Dacă totul funcționează corect, ar trebui să vedeți acest lucru în consola dezvoltatorului.

Asta e! În consola dezvoltatorului, puteți vedea că magazinul Redux este înregistrat, astfel încât să puteți vedea cum se schimbă după fiecare acțiune.

rezumat

Am creat o aplicație Redux pentru oase goale pentru aplicația minunată a listei noastre de contacte. Am aflat despre reductoare, împărțirea reducerilor pentru a face structura aplicației mai curată și scrierea de acțiuni pentru mutarea magazinului. 

Spre sfârșitul postului, ne-am abonat la magazin folosind store.subscribe () metodă. Din punct de vedere tehnic, aceasta nu este cea mai bună modalitate de a face lucrurile dacă intenționați să utilizați React cu Redux. Există mai multe modalități optimizate de conectare a frontului de reacție cu Redux. Vom acoperi cele din următorul tutorial.  

Cod