Komma igång med Redux Varför Redux?

När du lär dig React, kommer du nästan alltid att höra människor säga hur bra Redux är och att du ska prova. React-ekosystemet växer i snabb takt, och det finns så många bibliotek som du kan ansluta till React, såsom flöde, redux, middlewares, mobx, etc. 

Learning React är lätt, men att vänja sig på hela React-ekosystemet tar tid. Denna handledning är en introduktion till en av de integrerade komponenterna i React ecosystem-Redux.

Grundläggande Non-Redux Terminologi

Här är några av de vanliga terminologier som du kanske inte känner till, men de är inte specifika för Redux i sig. Du kan skumma igenom det här avsnittet och komma tillbaka hit när / om något inte är meningslöst.  

Ren funktion

En ren funktion är bara en normal funktion med ytterligare två begränsningar som den måste uppfylla: 

  1. Med en uppsättning ingångar bör funktionen alltid returnera samma utgång. 
  2. Det ger inga biverkningar.

Här är en ren funktion som returnerar summan av två siffror.

/ * Ren add-funktion * / const add = (x, y) => returnera x + y;  console.log (lägg till (2,3)) // 5 

Rena funktioner ger en förutsägbar utgång och är deterministiska. En funktion blir oren när den utför något annat än att beräkna sitt returvärde. 

Till exempel använder tilläggsfunktionen nedan ett globalt tillstånd för att beräkna dess produktion. Dessutom loggar funktionen också värdet till konsolen, vilket anses vara en bieffekt. 

const y = 10; const impureAdd = (x) => console.log ('Inmatningarna är $ x och $ y'); returnera x + y;  

Observerbara biverkningar

"Observable bieffekter" är en fancy term för interaktioner som gjorts av en funktion med omvärlden. Om en funktion försöker skriva ett värde i en variabel som existerar utanför funktionen eller försöker ringa en extern metod, kan du säkert kalla dessa saker biverkningar. 

Men om en ren funktion kallar en annan ren funktion, kan funktionen behandlas som ren. Här är några av de vanliga biverkningarna:

  • gör API-samtal
  • loggar till konsolen eller utskriftsdata
  • mutera data
  • DOM-manipulation
  • hämtar den aktuella tiden

Container och presentationsdetaljer

Att dela upp komponentarkitekturen i två är användbar när du arbetar med React-applikationer. Du kan i stor utsträckning klassificera dem i två kategorier: behållarkomponenter och presentationsdelar. De är också populärt kända som smarta och dumma komponenter. 

Behållarkomponenten handlar om hur sakerna fungerar, medan presentationsdelar är oroade över hur sakerna ser ut. För att förstå begreppen bättre har jag täckt det i en annan handledning: Container vs Presentational Components in React.

Mutable vs Immutable Objects

Ett muterbart objekt kan definieras enligt följande:

en muterbart föremål är ett objekt vars tillstånd kan modifieras efter det att det har skapats.

Immutability är exakt motsatsen-ett oföränderligt föremål är ett objekt vars tillstånd kan inte modifieras efter det att den har skapats. I JavaScript är strängar och siffror oföränderliga, men objekt och arrays är inte. Exemplet visar skillnaden bättre. 

/ * Strängar och siffror är oföränderliga * / låt a = 10; låt b = a; b = 3; console.log ('a = $ a och b = $ b'); // a = 10 och b = 3 / * Men objekt och arrays är inte * / / * Låt oss börja med objekt * / let användare = namn: "Bob", ålder: 22, jobb: "None" active_user = user ; active_user.name = "Tim"; // Båda objekten har samma värde console.log (active_user); // "namn": "Tim", "ålder": 22, "jobb": "None" console.log (användare); // "namn": "Tim", "ålder": 22, "jobb": "None" / * Nu för arrays * / let usersId = [1,2,3,4,5] låt usersIdDup = usersId ; usersIdDup.pop (); console.log (usersIdDup); // [1,2,3,4] console.log (usersId); // [1,2,3,4]

För att göra saker oföränderliga, använd Object.assign metod för att skapa en ny metod eller alla nya spridningsoperatörer.

låt användaren = namn: "Bob", ålder: 22, jobb: "None" active_user = Object.assign (, användare, namn: "Tim") console.log (användare); // "namn": "Bob", "ålder": 22, "jobb": "None" console.log (active_user); //  "namn": "Tim", "ålder": 22, "jobb": "Ingen" 

Vad är Redux?

På den officiella sidan definieras Redux enligt följande:

Redux är en förutsägbar tillståndsbehållare för JavaScript-applikationer. 

Även om det exakt beskriver Redux är det lätt att gå vilse när du ser den större bilden av Redux för första gången. Det har så många rörliga bitar som du behöver passa ihop. Men när du gör det lovar jag dig, du kommer börja älska Redux. 

Redux är ett statsförvaltningsbibliotek som du kan ansluta till något JavaScript-bibliotek, och inte bara React. Det fungerar dock mycket bra med React på grund av Reacts funktionella natur. För att förstå det här bättre, låt oss ta en titt på staten.

Som du kan se bestämmer en komponents tillstånd vad som görs och hur det beter sig. Applikationen har ett initialt tillstånd, och användarinteraktion utlöser en åtgärd som uppdaterar staten. När staten är uppdaterad, är sidan renad.

Med React har varje komponent en lokal stat som är tillgänglig från komponenten, eller du kan skicka dem ner som rekvisita till barnkomponenter. Vi brukar använda staten för att lagra:

  1. UI-tillstånd och övergångsdata. Detta inkluderar en lista med användargränssnitt för navigationsmenyn eller forminmatningar i en kontrollerad komponent.
  2. Applikationstillstånd, såsom data som hämtats från en server, användarens inloggningstillstånd osv.

Att lagra applikationsdata i en komponents tillstånd är okej när du har en grundläggande React-applikation med några komponenter. 

Komponenthierarkin för en grundläggande applikation

Men de flesta verkliga apps kommer att ha många fler funktioner och komponenter. När antalet nivåer i komponenthierarkin ökar blir hanteringen av staten problematisk. 

Skiss av en medelstor applikation

Varför ska du använda Redux?

Här är ett mycket troligt scenario som du kan stöta på när du arbetar med React.

  1. Du bygger en medelstor applikation, och du har dina komponenter snyggt indelade i smarta och dumma komponenter. 
  2. De smarta komponenterna hanterar tillståndet och skickar dem sedan till de dumma komponenterna. De tar hand om att göra API-samtal, hämtar data från datakällan, bearbetar data och ställer sedan in tillståndet. De dumma komponenterna får rekvisita och returnerar UI-representationen. 
  3. När du ska skriva en ny komponent är det inte alltid klart var du ska placera staten. Du kan låta staten vara en del av en behållare som är en omedelbar förälder till presentationsdelen. Bättre än, du kan flytta staten högre upp i hierarkin så att staten är tillgänglig för flera presentationsdelar.
  4. När appen växer ser du att staten är utspridda överallt. När en komponent behöver komma åt det tillstånd som den inte direkt har tillgång till, kommer du att försöka lyfta staten upp till närmaste komponentfader. 
  5. Efter konstant refactoring och städning slutar du med de flesta av statens hållplatser längst upp i komponenthierarkin. 
  6. Slutligen bestämmer du att det är en bra idé att låta en komponent i toppen hantera staten globalt och sedan skicka allt ner. Varje annan komponent kan prenumerera på rekvisita som de behöver och ignorera resten.

Detta är vad jag personligen har upplevt med React, och många andra utvecklare kommer överens om. React är ett visningsbibliotek, och det är inte Reacts jobb att specifikt hantera tillstånd. Det vi letar efter är principen om åtskillnad av oro. 

Redux hjälper dig att skilja applikationstillståndet från React. Redux skapar en global butik som ligger på översta nivån av din ansökan och matar staten till alla andra komponenter. Till skillnad från Flux har Redux inte flera affärsobjekt. Hela tillståndet för ansökan ligger inom det här objektet, och du kan eventuellt byta visningsskiktet med ett annat bibliotek med butiken intakt.

Komponenterna återställs varje gång butiken uppdateras, med mycket liten inverkan på prestanda. Det är goda nyheter, och det ger många fördelar med det. Du kan behandla alla dina React-komponenter som dumma, och React kan bara fokusera på utsikten av saker.

Nu när vi vet varför Redux är användbart, låt oss dyka in i Redux-arkitekturen.

Reduxarkitekturen

När du lär dig Redux finns det några grundläggande begrepp som du behöver vänja dig. Bilden nedan beskriver Redux-arkitekturen och hur allt är kopplat ihop. 

Redux i ett nötskal

Om du är van vid Flux kan vissa av elementen bli bekanta. Om inte, det är okej för att vi ska täcka allt från basen. Först, se till att du har installerat REDX:

npm installera redux

Använd Create-React-app eller din favorit webpack-konfiguration för att konfigurera utvecklingsservern. Eftersom Redux är en oberoende statlig förvaltning kommer vi inte att plugga in React än. Så ta bort innehållet i index.js, och vi ska leka med Redux för resten av denna handledning.

Lagra

Butiken är ett stort JavaScript-objekt som har massor av nyckelvärdespar som representerar aktuellt tillstånd för applikationen. Till skillnad från det statliga objektet i React som sprinklas över olika komponenter har vi bara en butik. Butiken ger applikationstillståndet, och varje gång staten uppdaterar, vyn ser rerenders. 

dock, Du kan aldrig mutera eller byta butik. Istället skapar du nya versioner av butiken. 

(tidigareState, action) => newState

På grund av detta kan du göra tidsresor genom alla stater från det att appen startades på din webbläsare.

Butiken har tre metoder att kommunicera med resten av arkitekturen. Dom är:

  • Store.getState () - För att komma åt nuvarande tillståndsträdet i din ansökan. 
  • Store.dispatch (action) - För att utlösa en tillståndsändring baserat på en åtgärd. Mer om åtgärder nedan.
  • Store.subscribe (lyssnare) -Här lyssna på någon ändring i staten. Det kommer att ringas varje gång en åtgärd skickas.

Låt oss skapa en butik. Redux har en createStore Metod för att skapa en ny butik. Du måste överföra det till en reducerare, även om vi inte vet vad det är. Så jag ska bara skapa en funktion som kallas reducerare. Du kan eventuellt ange ett andra argument som anger butikens inledande skick. 

src / index.js

importera createStore från "redux"; // Detta är reducer const reducer = () => / * Något går här * / // initialState är valfritt. // För denna demo använder jag en räknare, men vanligtvis är staten ett objekt const initialState = 0 const store = createStore (reducer, initialState);

Nu ska vi lyssna på några ändringar i butiken, och sedan console.log () Butikens nuvarande tillstånd.

store.subscribe (() => console.log ("State har ändrats" + store.getState ());) 

Så hur uppdaterar vi butiken? Redux har något som kallas handlingar som gör att detta händer.

Action / Action Skapare

Åtgärder är också vanliga JavaScript-objekt som skickar information från din ansökan till affären. Om du har en mycket enkel räknare med en inkrementsknapp, kommer det att resultera i en åtgärd som ser ut så här på följande sätt:

typ: "INCREMENT", nyttolast: 1

De är den enda informationskällan till affären. Butiken i butiken ändras endast som svar på en åtgärd. Varje åtgärd borde ha en typegenskap som beskriver vad åtgärdsobjektet avser att göra. Annat än det är handlingsstrukturen helt upp till dig. Håll dock din åtgärd liten eftersom en åtgärd representerar den minsta mängd information som krävs för att omvandla applikationstillståndet. 

Till exempel, i exemplet ovan är typegenskapen inställd på "INCREMENT", och en extra nyttolastningsegenskap ingår. Du kan omdöpa nyttolasten till något mer meningsfullt eller, i vårt fall, utelämna det helt. Du kan skicka en åtgärd till affären så här.

store.dispatch (typ: "INCREMENT", nyttolast: 1); 

Medan du kodar Redux, brukar du inte använda åtgärder direkt. I stället ringer du funktioner som returnerar handlingar, och dessa funktioner är allmänt kända som actionskapare. Här är handlingsskaparen för den inkrementåtgärd som vi diskuterade tidigare.

const incrementCount = (count) => return typ: "INCREMENT", nyttolast: count

Så, för att uppdatera statusen på räknaren måste du skicka incrementCount agera så här:

store.dispatch (incrementCount (1)); store.dispatch (incrementCount (1)); store.dispatch (incrementCount (1));

Om du går till webbläsarkonsolen ser du att den fungerar delvis. Vi blir odefinierade eftersom vi ännu inte har definierat reduceraren.

Så nu har vi täckt åtgärder och affären. Vi behöver dock en mekanism för att konvertera informationen som tillhandahålls av åtgärden och omvandla tillståndet i butiken. Reduktionsmedel tjänar detta syfte.

Reduktorer

En åtgärd beskriver problemet, och reduceraren är ansvarig för att lösa problemet. I det tidigare exemplet incrementCount metod returnerade en åtgärd som gav information om vilken typ av förändring som vi ville göra till staten. Reduktorn använder denna information för att faktiskt uppdatera tillståndet. Det finns en stor punkt markerad i dokumenten som du alltid ska komma ihåg när du använder Redux:

Med samma argument bör en reducerare beräkna nästa tillstånd och returnera det. Inga överraskningar. Inga biverkningar. Inga API-samtal. Inga mutationer. Bara en beräkning.

Vad detta innebär är att en reducerare ska vara en ren funktion. Med en uppsättning ingångar bör den alltid returnera samma utmatning. Utöver det borde det inte göra någonting mer. Dessutom är en reducer inte platsen för biverkningar som att göra AJAX-samtal eller hämta data från API. 

Låt oss fylla i reduceraren till vår räknare.

// Detta är reducer const reducer = (state = initialState, action) => switch (action.type) fall "INCREMENT": returstatus + action.payload standard: returstatus

Reduktorn accepterar två argument-state och action-och det returnerar ett nytt tillstånd.

(tidigareState, action) => newState

Staten accepterar ett standardvärde, initialtillstånd, som endast kommer att användas om statens värde är odefinierat. I annat fall kommer det faktiska värdet av staten att behållas. Vi använder omkopplingsdeklarationen för att välja rätt åtgärd. Uppdatera webbläsaren, och allt fungerar som förväntat. 

Låt oss lägga till ett ärende för MINSKNING, utan vilken räknaren är ofullständig.

// Detta är reducer const reducer = (state = initialState, action) => switch (action.type) fall "INCREMENT": returläge + action.payload case "DECREMENT": returstatus - action.payload default: returläge

Här är handlingsskaparen.

const decrementCount = (count) => return typ: "DECREMENT", nyttolast: count

Slutligen skicka det till affären.

store.dispatch (incrementCount (4)); // 4 store.dispatch (decrementCount (2)); // 2

Det är allt!

Sammanfattning

Denna handledning var tänkt att vara en utgångspunkt för att hantera staten med Redux. Vi har täckt allt som behövs för att förstå de grundläggande Redux-koncepten, såsom butiken, åtgärderna och reducerarna. Mot slutet av handledningen skapade vi också en arbetsreducerad demodisk. Även om det inte var så mycket lärde vi oss hur alla pusselbitarna passar ihop. 

Under de senaste åren har React vuxit i popularitet. Faktum är att vi har ett antal objekt på marknaden som är tillgängliga för köp, granskning, genomförande och så vidare. Om du letar efter ytterligare resurser runt React, tveka inte att kolla in dem.

I nästa handledning kommer vi att använda de saker som vi har lärt oss här för att skapa en React-applikation med Redux. Stanna stilla tills dess. Dela dina tankar i kommentarerna.