Komme i gang med Redux Lær ved eksempel

Redux hjelper deg med å håndtere tilstand ved å sette staten opp på et globalt nivå. I den forrige veiledningen hadde vi en god titt på Redux-arkitekturen og Redux-komponentene som for eksempel handlinger, handlingsskapere, butikken og reduksjonsmaskiner. 

I dette andre innlegget i serien skal vi styrke vår forståelse av Redux og bygge videre på det vi allerede vet. Vi starter med å skape en realistisk Redux-applikasjon - en kontaktliste - det er mer komplisert enn en grunnteller. Dette vil hjelpe deg å styrke din forståelse av single store og flere reduksjonsbegreper som jeg introduserte i den forrige opplæringen. Senere snakker vi om å binde din Redux-stat med en React-applikasjon og de beste praksiser som du bør vurdere når du lager et prosjekt fra grunnen av. 

Men det er greit hvis du ikke har lest det første innlegget. Du bør fortsatt kunne følge med så lenge du kjenner Redux grunnleggende. Koden for opplæringen er tilgjengelig i repo, og du kan bruke det som utgangspunkt. 

Opprette en kontaktliste ved hjelp av Redux

Vi skal bygge en grunnleggende kontaktliste med følgende funksjoner:

  • vis alle kontakter
  • søk etter kontakter
  • hente alle kontakter fra serveren
  • legg til en ny kontakt
  • skyv de nye kontaktdataene inn i serveren

Slik ser vår søknad ut:

Endelig produkt - Kontaktliste Vis


Endelig produkt - Legg til kontaktvisning

Det er vanskelig å dekke alt i en strekk. Så i dette innlegget kommer vi til å fokusere på bare Redux-delen av å legge til en ny kontakt og vise den nylig lagt til kontakten. Fra et Redux-perspektiv, vil vi initialisere staten, skape butikken, legge til redusere og tiltak, osv. 

I neste veiledning lærer vi hvordan du kobler til React og Redux og sender Redux-handlinger fra en React-frontend. I den siste delen skifter vi vårt fokus mot å lage API-anrop ved hjelp av Redux. Dette inkluderer å hente kontaktene fra serveren og foreta en serverforespørsel mens du legger til nye kontakter. Bortsett fra det, vil vi også opprette en søkefelt-funksjon som lar deg søke i alle eksisterende kontakter. 

Lag en skisse av State Tree

Du kan laste ned reagent-redux-demo-applikasjonen fra mitt GitHub-lager. Klone repoen og bruk v1 gren som utgangspunkt. De v1 gren er svært lik skape-reagere-app-malen. Den eneste forskjellen er at jeg har lagt til noen få tomme kataloger for å organisere Redux. Her er katalogstrukturen.

. ├── package.json ├── offentlige ├── README.md ├── src │ ├── handlinger │ ├── App.js │ ├── komponenter │ ├── containere │ ├── index.js │ ├── reduksjon │ └─ - lagre └── garn.lock 

Alternativt kan du opprette et nytt prosjekt fra bunnen av. Uansett må du ha installert en grunnleggende reaktorkjelett og redux før du kan komme i gang. 

Det er en god ide å ha en grov skisse av statens tre først. Etter min mening vil dette spare deg for mye tid i det lange løp. Her er en grov skisse av det mulige statstrøet. 

const initialState = kontakter: contactList: [], newContact: navn: ", etternavn:", e-post: ", adresse:", telefon: ", ui: // Alle brukergrensesnittene her. gjem / vis modaler, / / ​​veksleboks etc.  

Vår butikk må ha to egenskaper-kontakter og ui. Kontakteregenskapen tar seg av alle kontaktrelaterte tilstander, mens ui håndterer UI-spesifikk tilstand. Det er ingen vanskelig regel i Redux som hindrer deg i å plassere ui objekt som en delstat av kontakter. Du er velkommen til å organisere staten din på en måte som føles meningsfull for din søknad. 

Kontakteregenskapen har to egenskaper nestet inne i den-kontaktliste og ny kontakt. De kontaktliste er en rekke kontakter, mens ny kontakt Lagrer kontaktinformasjon midlertidig mens kontaktskjemaet fylles. Jeg skal bruke dette som utgangspunkt for å bygge vår fantastiske kontaktliste-app. 

Hvordan organisere Redux

Redux har ingen mening om hvordan du strukturerer søknaden din. Det er noen få populære mønstre der ute, og i denne opplæringen vil jeg kort snakke om noen av dem. Men du bør velge ett mønster og holde fast med det til du forstår helt hvordan alle brikkene er koblet sammen.

Det vanligste mønsteret som du finner, er filen Rails-stil og mappestruktur. Du har flere toppnivå kataloger som de nedenfor:

  • komponenter: Et sted å lagre de dumme React-komponentene. Disse komponentene bryr seg ikke om du bruker Redux eller ikke.
  • beholdere: En katalog for de smarte React-komponentene som sender handlinger til Redux-butikken. Bindingen mellom redux og reaksjon finner sted her. 
  • handlinger: Handlingsskaperne vil gå inn i denne katalogen. 
  • reduksjonsgir: Hver reduksjon får en individuell fil, og du vil plassere alle reduksjonslogikken i denne katalogen.
  • butikk: Logikken for å initialisere staten og konfigurere butikken vil gå her. 

Bildet nedenfor viser hvordan søknaden vår kan se ut hvis vi følger dette mønsteret:

Rails stilen bør fungere for små og mellomstore applikasjoner. Når appen ditt vokser, kan du imidlertid vurdere å bevege seg mot domenestilstilgangen eller andre populære alternativer som er nært relatert til domenestil. Her vil hver funksjon ha en egen katalog, og alt relatert til den funksjonen (domenet) vil være inne i det. Bildet nedenfor sammenligner de to tilnærmingene, Rails-stil til venstre og domenestil til høyre. 

For nå, gå videre og opprett kataloger for komponenter, beholdere, butikk, reduksjonsgir, og handling. La oss starte med butikken. 

Single Store, Multiple Reduksers

La oss lage en prototype forde butikk og redusering først. Fra vårt forrige eksempel, er slik butikken vår ser ut: 

const store = createStore (reduksjon, kontakter: kontaktliste: [], nyKontakt: , ui: isContactFormHidden: true) const reduer = (tilstand, handling) => bytt "HANDLE_INPUT_CHANGE": break; saken "ADD_NEW_CONTACT": break; saken "TOGGLE_CONTACT_FORM": break;  returstatus 

Bryteroppstillingen har tre saker som tilsvarer tre handlinger som vi skal skape. Her er en kort forklaring på hva handlingene er ment for. 

  • HANDLE_INPUT_CHANGE: Denne handlingen blir utløst når brukeren legger inn nye verdier i kontaktskjemaet.
  • ADD_NEW_CONTACT: Denne handlingen sendes når brukeren sender skjemaet.
  • TOGGLE_CONTACT_FORM: Dette er en UI-handling som tar seg av å vise / gjemme kontaktskjemaet. 

Selv om denne naive tilnærmingen fungerer, da søknaden vokser, vil denne teknikken ha noen mangler.

  1. Vi bruker en enkelt reduksjonsmaskin. Selv om en enkelt reduksjonsenhet lyder bra for nå, kan du forestille deg å ha all forretningslogikk under en veldig stor reduksjonsenhet.  
  2. Koden ovenfor følger ikke Redux-strukturen som vi har diskutert i forrige avsnitt.

For å fikse problemet med enkeltreduksjon, har Redux en metode som heter kombinere Reducerer som lar deg lage flere reduksjonsverktøy og deretter kombinere dem til en enkelt reduserende funksjon. Funksjonen combineReducers forbedrer lesbarheten. Så jeg skal dele reduksjonsmaskinen i to-a contactsReducer og a uiReducer

I eksemplet ovenfor, Create aksepterer en valgfri andre argumentet som er den opprinnelige tilstanden. Men hvis vi skal dele reduksjonene, kan vi flytte hele opprinnelige tilstand til en ny filplassering, si reduksjonsgir / initialState.js. Vi vil da importere en delmengde av opprinnelige tilstand inn i hver reduksjonsfil. 

Splitting Reduceer 

La oss omstrukturere koden vår for å fikse begge problemene. Først oppretter du en ny fil som heter store / createStore.js og legg til følgende kode:

importer createStore fra 'redux'; importer rootReducer fra '... / reducers /'; / * Opprett en funksjon kalt configureStore * / eksporter standardfunksjon configureStore () return createStore (rootReducer);  

Deretter oppretter du en rotreduksjon i reduksjonsgir / index.js som følger:

importer combineReducers fra 'redux' import contactsReducer fra './contactsReducer'; importer uiReducer fra './uiReducer'; const rootReducer = combineReducers (contacts: contactsReducer, ui: uiReducer,) eksporter standard rootReducer;

Til slutt må vi opprette koden for contactsReducer og uiReducer.

reduksjonsgir / contactsReducer.js

importer initialstate fra './initialState'; eksporter standardfunksjonskontaktReducer (state = initialState.contacts, action) switch (action.type) / * Legg til kontakter til statussystemet * / tilfelle "ADD_CONTACT": return ... state, contactList: [... state.contactList, state.newContact] / * Håndter inngang for kontaktskjemaet. Lastbelastningen (inngangsendringer) blir fusjonert med det nyeContact-objektet * / tilfellet "HANDLE_INPUT_CHANGE": retur ... state, newContact: ... state.newContact, ... action.payload standard: returstatus; 

reduksjonsgir / uiReducer.js

importer initialstate fra './initialState'; (standard : retur tilstand;  

Når du lager reduksjonsutstyr, vær alltid oppmerksom på følgende: En reduksjonsmaskin må ha en standardverdi for sin tilstand, og den må alltid returnere noe. Hvis reduksjonsbryteren ikke følger denne spesifikasjonen, vil du få feil.

Siden vi har dekket mye kode, la oss se på endringene vi har gjort med vår tilnærming:

  1. De combineReducers samtalen er innført for å knytte sammen spaltreduksjonene.
  2. Staten av ui objektet håndteres av uiReducer og tilstanden til kontaktene av contactsReducer
  3. For å holde reduksjonene rene, har spredningsoperatører blitt brukt. Trepunkts syntaks er en del av spredningsoperatøren. Hvis du ikke er komfortabel med spredningssyntaxen, bør du vurdere å bruke et bibliotek som Immutability.js.
  4. Den opprinnelige verdien er ikke lenger angitt som et valgfritt argument til Create. I stedet har vi opprettet en egen fil for den som heter initialState.js. Vi importerer opprinnelige tilstand og deretter angi standardstatus ved å gjøre det state = initialState.ui

Statsinitialisering

Her er koden for reduksjonsgir / initialState.js fil.

const initialState = kontakter: contactList: [], newContact: navn: ", etternavn:", e-post: ", adresse:", telefon: ",, ui: isContactFormHidden: true

Handlinger og handlingsskapere

La oss legge til et par handlinger og handlingsskapere for å legge til endringer i håndtering av skjemaer, legge til en ny kontakt og bytte til UI-tilstanden. Hvis du husker, er handlingskapere bare funksjoner som gir tilbake en handling. Legg til følgende kode i handlinger / index.js.

eksport const addContact = () => retur type: "ADD_CONTACT" eksport const handleInputChange = (navn, verdi) => retur type: "HANDLE_INPUT_CHANGE", nyttelast: [navn]: verdi eksporter const toggleContactForm = () => retur type: "TOGGLE_CONTACT_FORM",

Hver handling må returnere en type eiendom. Typen er som en nøkkel som bestemmer hvilken reduksjon blir påkalt og hvordan staten blir oppdatert som svar på den handlingen. Lastbelastningen er valgfri, og du kan faktisk kalle det alt du vil ha. 

I vårt tilfelle har vi opprettet tre handlinger.

De TOGGLE_CONTACT_FORM trenger ikke nyttelast fordi hver gang handlingen utløses, verdien av ui.isContactFormHidden blir byttet. Boolsk-verdsatt handlinger krever ikke en nyttelast. 

De HANDLE_INPUT_CHANGE Handlingen utløses når formverdien endres. Så for eksempel, tenk at brukeren fyller e-postfeltet. Handlingen mottar deretter "E" og "[email protected]" som innganger, og nyttelastet overført til reduksjonsmaskinen er en gjenstand som ser slik ut:

email: "[email protected]"

Reduksjonen bruker denne informasjonen til å oppdatere de aktuelle egenskapene til ny kontakt stat. 

Forsendelseshandlinger og abonnement på butikken

Det neste logiske trinnet er å sende handlingene. Når handlingene er sendt, endres staten som svar på det. For å sende handlinger og for å få oppdatert tilstandstreet, tilbyr Redux visse butikkhandlinger. De er:

  • avsendelse (handling): Sender en handling som potensielt kan utløse en tilstandsendring. 
  • getState (): Returnerer nåværende tilstandstreet i søknaden din.
  • abonnent (lytteren): En forandringslytter som blir kalt hver gang en handling sendes, og en del av statstreet endres. 

Hodet til index.js fil og importere configureStore funksjonen og de tre handlingene som vi opprettet tidligere:

Import Reakt fra 'reagere'; importer render fra 'react-dom'; importer app fra './App'; / * Import Redux butikk og handlingene * / import configureStore fra './store/configureStore'; importer toggleContactForm, handleInputChange fra './actions';

Deretter oppretter du en butikk objekt og legg til en lytter som logger statstreet hver gang en handling sendes:

const store = configureStore (); // Merk at abonnere () returnerer en funksjon for å avregistrere lytteren const unsubscribe = store.subscribe (() => console.log (store.getState ()))

Til slutt, send noen handlinger:

/ * Return isContactFormHidden returnerer false * / store.dispatch (toggleContactForm ()); / * Return isContactFormHidden returnerer false * / store.dispatch (toggleContactForm ()); / * oppdaterer tilstanden til kontaktpersonen.NyeKontakt objekt * / store.dispatch (handleInputChange ('email', '[email protected]')) abonnement;

Hvis alt fungerer riktig, bør du se dette i utviklerkonsollen.

Det er det! I utviklerkonsollen kan du se Redux-butikken bli logget, slik at du kan se hvordan den endres etter hver handling.

Sammendrag

Vi har opprettet en Redux-applikasjon for bare-bein til vårt fantastiske kontaktlisteprogram. Vi lærte om reduksjonsmidler, splitter reduksjonsapparater for å gjøre vår app struktur renere, og skrive handlinger for mutating butikken. 

Mot slutten av innlegget abonnerte vi på butikken ved hjelp av store.subscribe () metode. Teknisk er dette ikke den beste måten å få ting gjort hvis du skal bruke React with Redux. Det er flere optimerte måter å koble reaksjonsfronten til med Redux. Vi vil dekke dem i neste opplæring.