Komma igång med Redux Lär dig genom exempel

Redux hjälper dig att hantera tillstånd genom att ställa upp staten på global nivå. I den tidigare handledningen hade vi en bra titt på Redux-arkitekturen och Redux-integrerade komponenter, såsom åtgärder, actionskapare, butiken och reducerare. 

I denna andra post i serien kommer vi att förstärka vår förståelse av Redux och bygga på det som vi redan vet. Vi börjar med att skapa en realistisk Redux-applikation - en kontaktlista - som är mer komplex än en basdisk. Detta kommer att hjälpa dig att förstärka din förståelse för det enda butiks- och multipla reduktionsbegreppet som jag introducerade i den tidigare handledningen. Senare talar vi om att binda ditt Redux-tillstånd med en React-applikation och de bästa metoderna du bör tänka på när du skapar ett projekt från början. 

Men det är okej om du inte har läst den första posten - du borde fortfarande kunna följa med så länge du känner till Redux grunderna. Koden för handledningen finns i repo, och du kan använda det som utgångspunkt. 

Skapa en kontaktlista med Redux

Vi ska bygga en grundläggande kontaktlista med följande funktioner:

  • visa alla kontakter
  • söka efter kontakter
  • hämta alla kontakter från servern
  • lägg till en ny kontakt
  • tryck in den nya kontakten till servern

Så här ser vår ansökan ut:

Slutprodukt - Kontaktlista Visa


Slutprodukt - Lägg till kontaktvy

Att täcka allt i en sträcka är svårt. Så i det här inlägget kommer vi att fokusera på bara Redux-delen av att lägga till en ny kontakt och visa den nyligen tillagda kontakten. Ur ett Redux perspektiv kommer vi att initialisera staten, skapa butiken, lägga till reducerare och åtgärder osv. 

I nästa handledning lär vi dig hur du ansluter React och Redux och skickar Redux-åtgärder från ett React-front-end. I den sista delen skifter vi vårt fokus mot att göra API-samtal med hjälp av Redux. Detta inkluderar att hämta kontakterna från servern och göra en serverbegäran när du lägger till nya kontakter. Utöver det skapar vi också en sökfält som låter dig söka igenom alla befintliga kontakter. 

Skapa en skiss över statsträdet

Du kan ladda ner demo-programmet för re-redux från mitt GitHub-arkiv. Klon repo och använd v1 gren som utgångspunkt. De v1 gren är mycket lik den skapa-reagera app-mallen. Den enda skillnaden är att jag har lagt till några tomma kataloger för att organisera Redux. Här är katalogstrukturen.

. ├── package.json ├── public ├── README.md ── src │ ├── actions │ ├── App.js │ ├── komponenter │ ├── behållare │ ├── index.js │ ├── reduktionsmedel │ └── butik └── garn.lock 

Alternativt kan du skapa ett nytt projekt från början. Hur som helst måste du ha installerat en grundläggande reaktionspanna och redux innan du kan komma igång. 

Det är en bra idé att först ha en grov skiss av statsträdet. Enligt min mening kommer det här att spara mycket tid på lång sikt. Här är en grov skiss av det möjliga statsträdet. 

const initialState = contacts: contactList: [], newContact: namn: ", efternamn:", email: ", adress:", phone: ", ui: // Alla användarrelaterade tillstånd här. dölja / visa modaler, / / ​​växla kryssrutan etc.  

Vår butik behöver ha två fastigheter-kontakter och ui. Kontakteregenskapen tar hand om alla kontaktrelaterade stater, medan ui hanterar UI-specifikt tillstånd. Det finns ingen hård regel i Redux som förhindrar att du placerar ui objekt som en delstat av kontakter. Du är välkommen att organisera ditt land på ett sätt som känns meningsfullt för din ansökan. 

Kontakteregenskapen har två egenskaper inbäddat inuti den-kontaktlista och ny kontakt. De kontaktlista är en rad kontakter, medan ny kontakt tillfälligt lagrar kontaktuppgifter medan kontaktformuläret fylls. Jag ska använda detta som utgångspunkt för att bygga vår fantastiska kontaktlista app. 

Hur man organiserar Redux

Redux har ingen åsikt om hur du strukturerar din ansökan. Det finns några populära mönster där ute, och i denna handledning kommer jag kortfattat att prata om några av dem. Men du borde välja ett mönster och hålla fast det tills du förstår helt hur alla bitar är anslutna ihop.

Det vanligaste mönstret som du hittar är filen Rails-stil och mappstruktur. Du har flera kataloger på toppnivå som de nedan:

  • komponenter: En plats att lagra de dumma React-komponenterna. Dessa komponenter bryr sig inte om du använder Redux eller inte.
  • behållare: En katalog för de smarta React-komponenterna som skickar åtgärder till Redux-butiken. Bindningen mellan redux och reaktion kommer att äga rum här. 
  • åtgärder: Handlingsskaparna kommer att gå in i denna katalog. 
  • reducerare: Varje reducerare får en enskild fil, och du placerar all reduktionslogiken i den här katalogen.
  • Lagra: Logiken för att initialisera tillståndet och konfigurera butiken går här. 

Bilden nedan visar hur vår applikation kan se ut om vi följer detta mönster:

Rails stilen bör fungera för små och medelstora applikationer. När din app växer kan du dock överväga att flytta mot domänstilstilten eller andra populära alternativ som är nära relaterade till domänstil. Här kommer varje funktion att ha en egen katalog, och allt relaterat till den funktionen (domänen) kommer att vara inuti den. Bilden nedan jämför de två metoderna, Rails-style till vänster och domänstil till höger. 

För nu, fortsätt och skapa kataloger för komponenter, behållare, Lagra, reducerare, och verkan. Låt oss börja med affären. 

Enkel butik, flera reducerare

Låt oss skapa en prototyp förde Lagra och den Reducer först. Ur vårt tidigare exempel så ser vår butik ut: 

const store = createStore (reducerare, contacts: kontaktlista: [], newContact: , ui: isContactFormHidden: true) const reducer = (status, åtgärd) => switch (action.type) "HANDLE_INPUT_CHANGE": break; fallet "ADD_NEW_CONTACT": break; fallet "TOGGLE_CONTACT_FORM": break;  returläge; 

Omkopplingsdeklarationen har tre fall som motsvarar tre åtgärder som vi kommer att skapa. Här är en kort förklaring av vad åtgärderna är avsedda för. 

  • HANDLE_INPUT_CHANGE: Denna åtgärd utlöses när användaren matar in nya värden i kontaktformuläret.
  • ADD_NEW_CONTACT: Den här åtgärden skickas när användaren skickar in formuläret.
  • TOGGLE_CONTACT_FORM: Detta är en UI-åtgärd som tar hand om att visa / gömma kontaktformuläret. 

Även om detta naiva tillvägagångssätt fungerar, kommer applikationen att öka med hjälp av denna teknik några få brister.

  1. Vi använder en enda reducerare. Även om en enda reducer låter okej för nu, tänk dig att ha all din affärslogik under en mycket stor reducerare.  
  2. Koden ovan följer inte Redux-strukturen som vi har diskuterat i föregående avsnitt.

För att fixa problemet med enstaka reducerare har Redux en metod som heter kombinera Reducerare som låter dig skapa flera reducerare och sedan kombinera dem till en enda reducerande funktion. Funktionen combineReducers ökar läsbarheten. Så jag ska dela reduceraren i två-a contactsReducer och a uiReducer

I exemplet ovan, createStore accepterar en valfri andra argumentet som är det ursprungliga tillståndet. Men om vi ska dela upp reducerarna kan vi flytta hela initialtillstånd till en ny filplats, säg reduktorer / initialState.js. Vi ska sedan importera en delmängd av initialtillstånd in i varje reduktionsfil. 

Splitting Reducer 

Låt oss omstrukturera vår kod för att åtgärda båda problemen. Skapa först en ny fil som heter lagra / createStore.js och lägg till följande kod:

importera createStore från 'redux'; importera rootReducer från '... / reducers /'; / * Skapa en funktion som kallas configureStore * / export standardfunktion configureStore () return createStore (rootReducer);  

Skapa sedan en rotreducerare i reduktorer / index.js som följer:

importera combineReducers från 'redux' importkontakterReducer från './contactsReducer'; importera uiReducer från './uiReducer'; const rootReducer = combineReducers (kontakter: contactsReducer, ui: uiReducer,) exportera standard rootReducer;

Slutligen måste vi skapa koden för contactsReducer och uiReducer.

reduktorer / contactsReducer.js

importera initialstat från "./initialState"; exportera standardfunktionskontaktReducer (state = initialState.contacts, action) switch (action.type) / * Lägg till kontakter till state array * / case "ADD_CONTACT": return ... state, contactList: [... state.contactList, state.newContact] / * Hantera inmatning för kontaktformuläret. Återlösen (inmatningsändringar) slås samman med det nyaContact-objektet * / fallet "HANDLE_INPUT_CHANGE": returnera ... state, newContact: ... state.newContact, ... action.payload standard: returstatus; 

reduktorer / uiReducer.js

importera initialstat från "./initialState"; export default funktion uiReducer (state = initialState.ui, action) switch (action.type) / * Visa / dölj formuläret * / fallet "TOGGLE_CONTACT_FORM": return ... state, isContactFormHidden:! state.isContactFormHidden standard : returläge;  

När du skapar reduktionsdon, håll alltid följande i åtanke: en reducer behöver ha ett standardvärde för sitt tillstånd, och det behöver alltid återgå till något. Om reduceraren inte följer denna specifikation får du fel.

Eftersom vi har täckt mycket kod, låt oss ta en titt på de förändringar som vi har gjort med vårt tillvägagångssätt:

  1. De combineReducers samtal har införts för att knyta samman delningsreducerarna.
  2. Statens tillstånd ui objekt kommer att hanteras av uiReducer och tillståndet för kontakterna av contactsReducer
  3. För att hålla reducerarna rena har spridningsoperatörer använts. Synpunkten för tre punkter är en del av spridningsoperatören. Om du inte är bekväm med spridningssyntaxen bör du överväga att använda ett bibliotek som Immutability.js.
  4. Det ursprungliga värdet anges inte längre som ett valfritt argument till createStore. Istället har vi skapat en separat fil för den som heter initialState.js. Vi importerar initialtillstånd och sedan ställa in standardläget genom att göra det state = initialState.ui

State Initialization

Här är koden för reduktorer / initialState.js fil.

const initialState = contacts: contactList: [], newContact: namn: ", efternamn:", email: ", adress:", telefon: ",, ui: isContactFormHidden: true

Åtgärder och aktionsskapare

Låt oss lägga till ett par handlingar och actionskapare för att lägga till hanteringsformulärändringar, lägga till en ny kontakt och växla till UI-tillståndet. Om du minns är åtgärdsskapare bara funktioner som ger en åtgärd. Lägg till följande kod i åtgärder / index.js.

export const addContact = () => return typ: "ADD_CONTACT" export consthandInputChange = (namn, värde) => return typ: "HANDLE_INPUT_CHANGE", nyttolast: [name]: value export const toggleContactForm = () => return typ: "TOGGLE_CONTACT_FORM",

Varje åtgärd måste returnera en typegenskap. Typen är som en nyckel som bestämmer vilken reducerare som åberopas och hur staten blir uppdaterad som svar på den åtgärden. Lastlasten är valfri, och du kan faktiskt kalla det du vill ha. 

I vårt fall har vi skapat tre åtgärder.

De TOGGLE_CONTACT_FORM behöver inte nyttolast eftersom varje gång åtgärden utlöses, värdet av ui.isContactFormHidden får växla. Booleskt värderade åtgärder kräver ingen nyttolast. 

De HANDLE_INPUT_CHANGE åtgärd utlöses när formvärdet ändras. Så tänk dig till exempel att användaren fyller i e-postfältet. Åtgärden tar då emot "e-post" och "[email protected]" som ingångar och nyttolasten överlämnas till reduceraren är ett objekt som ser ut så här:

email: "[email protected]"

Reduktorn använder denna information för att uppdatera de relevanta egenskaperna hos ny kontakt stat. 

Sändningsåtgärder och prenumerera på affären

Nästa logiska steg är att skicka åtgärderna. När åtgärderna har skickats ändras staten som svar på det. För att skicka åtgärder och för att få det uppdaterade tillståndstreet, erbjuder Redux vissa affärshandlingar. Dom är:

  • avsändande (verkan): Skickar en åtgärd som potentiellt kan utlösa en tillståndsändring. 
  • getState (): Returnerar nuvarande tillståndsträdet i din ansökan.
  • abonnent (lyssnare): En förändringslyttare som kallas varje gång en åtgärd skickas och en del av statsträdet ändras. 

Gå till index.js fil och importera configureStore funktionen och de tre åtgärder som vi skapade tidigare:

Import Reakt från "reagera"; importera render från 'react-dom'; importera app från './App'; / * Importera Redux butik och åtgärderna * / import configureStore från './store/configureStore'; importera toggleContactForm, handleInputChange från './actions';

Skapa sedan en Lagra objekt och lägg till en lyssnare som loggar statsträdet varje gång en åtgärd skickas:

const store = configureStore (); // Observera att prenumerera () returnerar en funktion för att avregistrera lyssnaren const unsubscribe = store.subscribe (() => console.log (store.getState ()))

Slutligen skicka några åtgärder:

/ * returnerar ärContactFormHidden returnerar false * / store.dispatch (toggleContactForm ()); / * returnerar ärContactFormHidden returnerar false * / store.dispatch (toggleContactForm ()); / * uppdaterar tillståndet av contacts.newContact object * / store.dispatch (handleInputChange ('email', '[email protected]')) avbryt prenumerationen;

Om allt fungerar rätt borde du se detta i utvecklarkonsolen.

Det är allt! I utvecklarkonsolen kan du se Redux-butiken som är inloggad så att du kan se hur den ändras efter varje åtgärd.

Sammanfattning

Vi har skapat en Redux-applikation för benen för vår fantastiska kontaktlista. Vi lärde oss om reduktionsmedel, splittring av reduktionsmedel för att göra vår appstruktur renare och skriva åtgärder för att mutera butiken. 

Mot slutet av stolpen abonnerade vi på affären med hjälp av store.subscribe () metod. Tekniskt är det inte det bästa sättet att få saker att göra om du ska använda React with Redux. Det finns mer optimerade sätt att ansluta React front-end med Redux. Vi täcker dem i nästa handledning.