10 tip til bedre redux-arkitektur

Mandarin Duck - Malcolm Carlaw (CC-BY-2.0)

Da jeg begyndte at bruge React, var der ingen Redux. Der var kun Flux-arkitekturen og omkring et dusin konkurrerende implementeringer af den.

Nu er der to klare vindere for datastyring i React: Redux og MobX, og sidstnævnte er ikke engang en Flux-implementering. Redux har fanget så meget, at det ikke bare bruges til React længere. Du kan finde Redux-arkitekturimplementeringer til andre rammer, inklusive Angular 2. Se f.eks. Ngrx: store.

Side note: MobX er sejt, og jeg ville sandsynligvis vælge det frem for Redux til enkle UI'er, fordi det er mindre kompliceret og mindre ordligt. Når det er sagt, er der nogle vigtige funktioner i Redux, som MobX ikke giver dig, og det er vigtigt at forstå, hvad disse funktioner er, før du beslutter, hvad der er rigtigt til dit projekt.
Bemærkning fra siden: Relay og Falcor er andre interessante løsninger til statsstyring, men i modsætning til Redux og MobX, skal de understøttes af henholdsvis GraphQL og Falcor Server, og al relay-tilstand svarer til nogle server-vedvarende data. AFAIK tilbyder hverken en god historie til kun side om side, forbigående statsstyring. Du kan muligvis nyde fordelene ved både at blande og matche Relay eller Falcor med Redux eller MobX ved at skelne mellem klient-kun-tilstand og server-vedvarende tilstand. Nederste linje: Der er ingen klar enkelt vinder for statsledelse på klienten i dag. Brug det rigtige værktøj til det aktuelle job.

Dan Abramov, skaberen af ​​Redux lavede et par gode kurser om emnet:

  • Kom godt i gang med Redux
  • Bygning af applikationer med Idiomatic Redux

Begge er gode trin-for-trin-tutorials, der forklarer Redux-basics, men du har også brug for en bedre forståelse for at få mest muligt ud af Redux.

Følgende er tip, der hjælper dig med at opbygge bedre Redux-apps.

1. Forstå fordelene ved Redux

Der er et par vigtige mål for Redux, som du skal huske på:

  1. Deterministiske syn gengivelser
  2. Deterministisk tilstands reproduktion

Determinisme er vigtigt for applikationstestbarhed og diagnosticering og rettelse af fejl. Hvis dine applikationsvisninger og -tilstand ikke er bestemmende, er det umuligt at vide, om synspunkter og tilstand altid er gyldige. Du kan endda sige, at nondeterminisme er en fejl i sig selv.

Men nogle ting er i sig selv nondeterministiske. Ting som timingen af ​​brugerinput og netværks I / O. Så hvordan kan vi nogensinde vide, om vores kode virkelig fungerer? Let: Isolering.

Hovedformålet med Redux er at isolere tilstandsstyring fra I / O-bivirkninger såsom at gengive visningen eller arbejde med netværket. Når bivirkninger isoleres, bliver kode meget mere enkel. Det er meget nemmere at forstå og teste din forretningslogik, når det ikke hele er sammenfiltret med netværksanmodninger og DOM-opdateringer.

Når din visnings gengivelse er isoleret fra netværk I / O og tilstandsopdateringer, kan du opnå en deterministisk visnings gengivelse, hvilket betyder: Givet den samme tilstand, vil visningen altid give den samme output. Det eliminerer muligheden for problemer som racebetingelser fra asynkrone ting, der tilfældigt udsletter bittes af dit syn eller lemlæser bits af din tilstand, da dit synspunkt er i færd med at gengives.

Når en nybegynder tænker på at oprette en visning, kan de tænke, ”Denne bit har brug for brugermodellen, så jeg starter en async-anmodning om at hente det, og når det løfte løses, opdaterer jeg brugerkomponenten med deres navn. Den bit derovre kræver to-do-tingene, så vi henter det, og når løftet løser sig, løber vi over dem og trækker dem til skærmen. ”

Der er et par større problemer med denne tilgang:

  1. Du har aldrig alle de data, du har brug for, for at give den komplette visning på ethvert givet tidspunkt. Du begynder faktisk ikke at hente data, før komponenten begynder at gøre sine ting.
  2. Forskellige hentningsopgaver kan komme ind på forskellige tidspunkter, idet de subtile ændrer rækkefølgen af, at ting sker i visningen gengives sekvens. For virkelig at forstå render-sekvensen skal du have kendskab til noget, du ikke kan forudsige: varigheden af ​​hver async-anmodning. Popquiz: I det ovenstående scenarie, hvad gengives først, brugerkomponenten eller to-do-objekter? Svar: Det er et løb!
  3. Nogle gange vil begivenhedslyttere mutere visningstilstanden, hvilket kan udløse en anden gengivelse, hvilket komplicerer sekvensen yderligere.

Det centrale problem med at gemme dine data i visningstilstanden og give async begivenhedslyttere adgang til at mutere denne visningstilstand er dette:

“Nondeterminism = parallel behandling + delt tilstand”
~ Martin Odersky (Scala-designer)
Blanding af hentning af data, manipulation af data og synspunkter giver anledning til bekymring er en opskrift på tidsrejsende spaghetti.

Jeg ved, det lyder ret cool på en sci-fi-B-film på en måde, men tro mig, tidsrejsende spaghetti er den værste smagende slags der er!

Hvad fluxarkitekturen gør er at håndhæve en streng adskillelse og sekvens, der adlyder disse regler hver gang:

  1. Først kommer vi i en kendt, fast tilstand ...
  2. Så gengiver vi udsigten. Intet kan ændre staten igen for denne render loop.
  3. I betragtning af den samme tilstand vil visningen altid gengives på samme måde.
  4. Begivenhedslyttere lytter til brugerinput og netværksanmodningshåndterere. Når de får dem, sendes handlinger til butikken.
  5. Når en handling sendes, opdateres staten til en ny kendt tilstand, og sekvensen gentages. Kun udsendte handlinger kan berøre staten.

Det er Flux i et nøddeskal: En envejs dataflowarkitektur til dit brugergrænseflade:

Flux Arkitektur

Med Flux-arkitekturen lytter visningen til brugerinput og oversætter dem til actionobjekter, der sendes til butikken. Butikken opdaterer applikationstilstanden og giver besked om visningen, der skal gengives igen. Naturligvis er visningen sjældent den eneste kilde til input og begivenheder, men det er ikke noget problem. Yderligere begivenhedslyttere udsender handlingsobjekter, ligesom visningen:

Det er vigtigt, at tilstandsopdateringer i Flux er transaktioner. I stedet for blot at kalde en opdateringsmetode på staten eller direkte manipulere en værdi, sendes actionobjekter til butikken. Et handlingsobjekt er en transaktionspost. Du kan tænke på det som en banktransaktion - en oversigt over ændringen, der skal foretages. Når du indbetaler til din bank, bliver din saldo for 5 minutter siden ikke udslettet. I stedet føjes en ny saldo til transaktionshistorikken. Handlingsobjekter tilføjer en transaktionshistorik til din applikationstilstand.

Handlingsobjekter ser sådan ud:

Hvilke handlingsobjekter giver dig er muligheden for at føre en kørende log over alle statstransaktioner. Denne log kan bruges til at gengive staten på en deterministisk måde, hvilket betyder:

I betragtning af den samme starttilstand og de samme transaktioner i samme rækkefølge, får du altid den samme tilstand som et resultat.

Dette har vigtige konsekvenser:

  1. Let testbarhed
  2. Nem fortryd / forny
  3. Debugging af tidsrejser
  4. Holdbarhed - Selv hvis staten bliver udslettet, hvis du har en fortegnelse over hver transaktion, kan du gengive den.

Hvem vil ikke have en beherskelse over rum og tid? Transaktionsstatus giver dig tidsrejser supermagter:

Redux dev-værktøjets skyderen til skyderen

2. Nogle apps behøver ikke redux

Hvis din UI-arbejdsgang er enkel, kan alt dette være overdrevent. Hvis du laver et tic-tac-toe-spil, har du virkelig brug for fortryd / gentag? Spilene varer sjældent mere end et minut. Hvis brugeren skruer op, kan du bare nulstille spillet og lade dem starte forfra.

Hvis:

  • Brugernes arbejdsgange er enkle
  • Brugere samarbejder ikke
  • Du behøver ikke at administrere serversidenbegivenheder (SSE) eller websockets
  • Du henter data fra en enkelt datakilde per visning

Det kan være, at rækkefølgen af ​​begivenheder i appen sandsynligvis er tilstrækkelig enkel til, at fordelene ved transaktionsstatus ikke er værd at den ekstra indsats.

Måske behøver du ikke at fluxificere din app. Der er en meget enklere løsning til sådanne apps. Tjek MobX.

Efterhånden som kompleksiteten af ​​din app vokser, når kompleksiteten af ​​visningsstatestyring vokser, vokser værdien af ​​transaktionsstatus med den, og MobX giver ikke transaktionsstatestyring ud af boksen.

Hvis:

  • Brugernes arbejdsgange er komplekse
  • Din app har en lang række brugerarbejdsgange (overvej både almindelige brugere og administratorer)
  • Brugere kan samarbejde
  • Du bruger webstik eller SSE
  • Du indlæser data fra flere slutpunkter for at opbygge en enkelt visning

Du kan drage fordel af en transaktionsmodel til at gøre det umagen værd. Redux passer måske godt for dig.

Hvad har webstik og SSE at gøre med dette? Når du tilføjer flere kilder til asynkron I / O, bliver det sværere at forstå, hvad der foregår i appen med ubestemmelig tilstandsstyring. Bestemmelsesstatus og en oversigt over statstransaktioner forenkler apps som dette radikalt.

Efter min mening involverer de fleste store SaaS-produkter mindst et par komplekse UI-arbejdsgange og skal bruge transaktionsstatestyring. De fleste små hjælpeprogrammer og enkle prototyper burde ikke. Brug det rigtige værktøj til jobbet.

3. Forstå reducere

Redux = Flux + funktionel programmering

Flux foreskriver envejs dataflow og transaktionsstatus med handlingsobjekter, men siger ikke noget om, hvordan man håndterer handlingobjekter. Det er her Redux kommer ind.

Den primære byggesten til Redux statsstyring er reduktionsfunktionen. Hvad er en reduktionsfunktion?

I funktionel programmering bruges den almindelige hjælpeprogram 'reducer ()' eller 'fold ()' til at anvende en reduceringsfunktion på hver værdi i en liste over værdier for at akkumulere en enkelt outputværdi. Her er et eksempel på en summeringsreduktion anvendt på en JavaScript-matrix med `Array.prototype.reduce ()`:

I stedet for at operere på arrays anvender Redux reduktionsredskaber til en strøm af actionobjekter. Husk, et handlingsobjekt ser sådan ud:

Lad os dreje summeringsreduktionen ovenfor til en Redux-stil reducer:

Nu kan vi anvende det til nogle testhandlinger:

4. Reducere skal være rene funktioner

For at opnå deterministisk tilstandsgengivelse skal reduktionsmidler være rene funktioner. Ingen undtagelser. En ren funktion:

  1. Givet den samme input returnerer altid den samme output.
  2. Har ingen bivirkninger.

Vigtigt i JavaScript overføres alle ikke-primitive objekter til funktioner som referencer. Med andre ord, hvis du videregiver et objekt og derefter direkte muterer en egenskab på det objekt, ændres objektet også uden for funktionen. Det er en bivirkning. Du kan ikke kende den fulde betydning af at kalde funktionen uden også at vide den fulde historie for det objekt, du har sendt i. Det er dårligt.

I stedet for skal reducere reducere et nyt objekt. Det kan du f.eks. Gøre med 'Object.assign ({}, tilstand, {tingToChange})'.

Array-parametre er også referencer. Du kan ikke bare `.push ()` nye elementer til en matrix i en reducer, fordi `.push ()` er en mutationshandling. Tilsvarende er også `.pop ()`, `.shift ()`, `.unshift ()`, `.reverse ()`, `.plice ()` og enhver anden mutatormetode.

Hvis du vil være sikker med arrays, skal du begrænse de handlinger, du udfører på staten, til de sikre adgangsmetoder. I stedet for `.push ()`, skal du bruge `.concat ()`.

Se på 'ADD_CHAT'-sagen i denne chatreducer:

Som du kan oprette, oprettes et nyt objekt med 'Object.assign ()', og vi tilføjer arrayet med '.concat ()' i stedet for '.push ()'.

Personligt kan jeg ikke lide at bekymre mig om, at jeg ved en fejltagelse muterer min tilstand, så for nylig har jeg eksperimenteret med at bruge uforanderlige data API'er med Redux. Hvis min tilstand er et uforanderligt objekt, behøver jeg ikke engang at se på koden for at vide, at objektet ikke bliver muteret ved et uheld. Jeg kom til denne konklusion efter at have arbejdet på et team og opdaget fejl fra utilsigtede tilstandsmutationer.

Der er meget mere til rene funktioner end dette. Hvis du vil bruge Redux til produktionsapps, har du virkelig brug for et godt greb om, hvad rene funktioner er, og andre ting, du skal være opmærksom på (som f.eks. Håndtering af tid, logning og tilfældige tal). For mere om dette, se "Master JavaScript-interviewet: Hvad er en ren funktion?".

5. Husk: Reducere skal være den eneste kilde til sandhed

Alle tilstande i din app skal have en enkelt kilde til sandhed, hvilket betyder, at staten gemmes et enkelt sted, og hvor som helst andre steder, den stat er nødvendig, skal have adgang til staten med henvisning til dens eneste sandhedskilde.

Det er OK at have forskellige kilder til sandhed til forskellige ting. For eksempel kan URL'en være den eneste sandhedskilde for brugeranmodningens sti og URL-parametre. Måske har din app en konfigurationstjeneste, som er den eneste sandhedskilde for dine API-URL'er. Det er fint. Imidlertid…

Når du gemmer en tilstand i en Redux-butik, skal enhver adgang til denne tilstand ske via Redux. Manglende overholdelse af dette princip kan resultere i uaktuelle data eller den slags delte tilstandsmutationsfejl, som Flux og Redux blev opfundet til at løse.

Med andre ord, uden det eneste kilde til sandhedsprincippet, taber du potentielt:

  • Deterministisk synspunkt gengives
  • Deterministisk reproduktionsstatus
  • Nem fortryd / forny
  • Debugging af tidsrejser
  • Let testbarhed

Enten Redux eller ikke Redux din tilstand. Hvis du gør det halvvejs, kan du fortryde alle fordelene ved Redux.

6. Brug konstanter til handlingstyper

Jeg kan godt lide at sikre, at handlinger er lette at spore til den reducering, der beskæftiger dem, når du ser på handlingshistorikken. Hvis alle dine handlinger har korte, generiske navne som "CHANGE_MESSAGE", bliver det sværere at forstå, hvad der foregår i din app. Men hvis handlingstyper har mere beskrivende navne som "CHAT :: CHANGE_MESSAGE", er det naturligvis meget mere tydeligt, hvad der foregår.

Hvis du opretter en skrivefejl og sender en udefineret handlingskonstant, kaster appen en fejl for at advare dig om fejlen. Hvis du opretter en skrivefejl med en handlingstypestreng, mislykkes handlingen lydløst.

Opbevaring af alle handlingstyper for en reducer samlet på et sted øverst i filen kan også hjælpe dig:

  • Hold sammenhængende navne
  • Forstå hurtigt reduktions-API'et
  • Se hvad der er ændret i pull-anmodninger

7. Brug handlingskabere til at afkoble handlingslogik fra afsendelsesopkaldere

Når jeg fortæller folk, at de ikke kan generere id'er eller gribe den aktuelle tid i en reducer, får jeg et sjovt udseende. Hvis du stirrer mistænkeligt på din skærm lige nu, kan du være sikker: Du er ikke alene.

Så hvor er et godt sted at håndtere uren logik som den uden at gentage den overalt, hvor du har brug for at bruge handlingen? I en action-skaber.

Handlingsskabere har også andre fordele:

  • Hold konstanter af handlingstype indkapslet i din reduceringsfil, så du ikke behøver at importere dem andre steder.
  • Foretag nogle beregninger af input før afsendelsen af ​​handlingen.
  • Reducer kedelpladen

Lad os bruge en handlingsskaber til at generere handlingsobjektet ADD_CHAT:

Som du kan se ovenfor, bruger vi cuid til at generere tilfældige id'er for hver chatbesked og 'Date.now ()' til at generere tidsstemplet. Begge disse er uren operationer, som ikke er sikre at køre i reduceren - men det er helt OK at køre dem i action-skabere.

Reducer kedelpladen med handlingskabere

Nogle mennesker tror, ​​at brug af action-skabere tilføjer kedelpladen til projektet. Tværtimod er du ved at se, hvordan jeg bruger dem til at reducere kedelpladen i mine reducere kraftigt.

Tip: Hvis du gemmer dine konstanter, reduceringer og handlingskabere alle i den samme fil, reducerer du den nødvendige kedelplade, når du importerer dem fra separate placeringer.

Forestil dig, at vi vil tilføje en chatbruger mulighed for at tilpasse deres brugernavn og tilgængelighedsstatus. Vi kunne tilføje et antal handterere af handlingstypen til reduktionsmaskinen på denne måde:

For større reduktionsmaskiner kan dette vokse til en masse kedelplade. Masser af reduktionsmaskiner, jeg har bygget, kan blive meget mere komplekse end det, med masser af overflødige kode. Hvad hvis vi kunne sammenkæde alle de enkle handlinger til ændring af ejendom sammen?

Det viser sig, det er let:

Selv med den ekstra afstand og den ekstra kommentar er denne version kortere - og det er kun to tilfælde. Besparelserne kan virkelig tilføjes.

Er det ikke skift ... sag farlig? Jeg ser et fald igennem!

Du har måske læst et eller andet sted, at 'switch'-udsagn skal undgås, specifikt så vi kan undgå utilsigtet falde igennem, og fordi listen over sager kan blive oppustet. Du har måske hørt, at du aldrig burde bruge falde igennem med vilje, fordi det er svært at fange utilsigtede falde gennem fejl. Det er alt godt råd, men lad os tænke grundigt over de farer, jeg nævnte ovenfor:

  • Reduceringsmidler er sammensatte, så oppustning af kasser er ikke et problem. Hvis din liste over sager bliver for stor, skal du afbryde stykker og flytte dem i separate reduktionsredskaber.
  • Hvert tilfælde krop vender tilbage, så utilsigtet falde gennem bør aldrig ske. Ingen af ​​de grupperede falder gennem sager bør have andre organer end den, der udfører fangsten.

Redux bruger 'switch..case' godt. Jeg ændrer officielt mit råd om sagen. Så længe du følger de enkle regler ovenfor (hold switches små og fokuserede og vender tilbage fra alle tilfælde med sin egen krop), er 'switch'-udsagn fine.

Du har måske bemærket, at denne version kræver en anden nyttelast. Det er her dine action-skabere kommer ind:

Som du kan se, foretager disse handlingskabere oversættelsen mellem argumenterne og tilstandsformen. Men det er ikke alt, hvad de laver ...

8. Brug ES6-parameterindstillinger til signaturdokumentation

Hvis du bruger Tern.js med et editor-plugin (tilgængeligt for populære redaktører som Sublime Text og Atom), vil det læse disse ES6-standardopgaver og udlede den krævede grænseflade for dine handlingskabere, så når du ringer til dem, du kan få intelligens og autofuldførelse. Dette fjerner kognitiv belastning af udviklere, fordi de ikke behøver at huske den krævede nyttelasttype eller kontrollere kildekoden, når de glemmer det.

Hvis du ikke bruger en type inference-plugin som Tern, TypeScript eller Flow, skal du være det.

Bemærk: Jeg foretrækker at stole på slutninger leveret af standardopgaver synlige i funktionssignaturen i modsætning til typeanmærkninger, fordi:

  1. Du behøver ikke at bruge en Flow eller TypeScript for at få det til at fungere: I stedet bruger du standard JavaScript.
  2. Hvis du bruger TypeScript eller Flow, er kommentarer overflødige med standardtildelinger, fordi både TypeScript og Flow udleder typen fra standardtildelingen.
  3. Jeg finder det meget mere læseligt, når der er mindre syntaksstøj.
  4. Du får standardindstillinger, hvilket betyder, at selvom du ikke stopper CI-bygningen på typefejl (du vil blive overrasket, masser af projekter gør det ikke), har du aldrig en utilsigtet 'udefineret' parameter, der lurer i din kode.

9. Brug vælgere til beregnet tilstand og afkobling

Forestil dig, at du bygger den mest komplekse chat-app i historien til chat-apps. Du har skrevet 500k kodelinjer, og Derefter kaster produktgruppen et nyt funktionskrav på dig, der vil tvinge dig til at ændre datastrukturen i din tilstand.

Ingen grund til at få panik. Du var smart nok til at afkoble resten af ​​appen fra formen på din tilstand med vælgere. Kugle: undvundet.

For næsten enhver reduktion, jeg skriver, opretter jeg en vælger, der blot eksporterer alle de variabler, jeg har brug for for at konstruere visningen. Lad os se, hvordan det kan se ud for vores enkle chatreducerende:

eksport const getViewState = state => state;

Ja det ved jeg. Det er så simpelt, at det ikke engang er værd at være en værst. Du tænker måske, at jeg er vanvittig nu, men husker du den kugle, vi forhindrede os før? Hvad hvis vi ville tilføje en beregnet tilstand, som en komplet liste over alle de brugere, der har chattet under denne session? Lad os kalde det 'recentActiveUsers'.

Disse oplysninger er allerede gemt i vores nuværende tilstand - men ikke på en måde, der er let at få fat i. Lad os gå foran og få fat i det i 'getViewState ()':

Hvis du lægger alle dine beregnede tilstande i vælgere, skal du:

  1. Reducer kompleksiteten af ​​dine reducere og komponenter
  2. Frakobl resten af ​​din app fra din tilstandsform
  3. Overhold den eneste kilde til sandhedsprincippet, også inden for din reducer

10. Brug TDD: Skriv test først

Mange undersøgelser har sammenlignet test-først med test-efter-metodologier og overhovedet ingen test. Resultaterne er klare og dramatiske: De fleste af undersøgelserne viser mellem 40-80% reduktion i forsendelsespor som et resultat af skrivningstest, før du implementerer funktioner.

TDD kan effektivt skære din forsendelsesfejl i halvdelen, og der er masser af bevis for at sikkerhedskopiere denne påstand.

Mens jeg skrev eksemplerne i denne artikel, startede jeg dem alle med enhedstest.

For at undgå skrøbelige tests oprettede jeg følgende fabrikker, som jeg plejede at producere forventninger:

Bemærk, at begge leverer standardværdier, hvilket betyder, at jeg kan tilsidesætte egenskaber individuelt for kun at oprette de data, jeg er interesseret i til en bestemt test.

Sådan brugte jeg dem:

Bemærk: Jeg bruger bånd til enhedstest på grund af dets enkelhed. Jeg har også 2-3 års erfaring med Mocha og Jasmine og diverse erfaringer med mange andre rammer. Du skal være i stand til at tilpasse disse principper til de rammer, du vælger.

Bemærk den stil, jeg har udviklet til at beskrive indlejrede tests. Sandsynligvis på grund af min baggrund ved hjælp af Jasmine og Mocha, kan jeg godt lide at starte med at beskrive den komponent, jeg tester i en ydre blok, og derefter i indre blokke, beskrive, hvad jeg sender til komponenten. Inde inde laver jeg enkle ækvivalenspåstander, som du kan gøre med dit testbiblioteks 'deepEqual ()' eller 'toEqual ()' -funktioner.

Som du kan se, bruger jeg isolerede testtilstands- og fabriksfunktioner i stedet for værktøjer som `beforeEach ()` og `afterEach ()`, som jeg undgår, fordi de kan tilskynde uerfarne udviklere til at ansætte delt tilstand i testsuiten (det er dårligt) .

Som du sandsynligvis har gættet, har jeg tre forskellige slags test til hver reducer:

  1. Direkte reduktionsforsøg, som du lige har set et eksempel på. Disse tester i det væsentlige, at reduceren producerer den forventede standardtilstand.
  2. Handlingskabertest, som tester hver handlingsskaber ved at anvende reduceren til handlingen ved hjælp af en forudbestemt tilstand som udgangspunkt.
  3. Selektortest, som tester vælgerne for at sikre, at alle forventede egenskaber er der, inklusive beregne egenskaber med forventede værdier.

Du har allerede set en reduktionstest. Lad os se på nogle andre eksempler.

Action Creator Tests

Dette eksempel er interessant af et par grunde. Handlingsskaberen 'addChat ()' er ikke ren. Det betyder, at medmindre du angiver værdioverskridelser, kan du ikke foretage en bestemt forventning til alle de producerede egenskaber. For at tackle dette brugte vi et rør, som jeg undertiden bruger for at undgå at oprette ekstra variabler, som jeg ikke rigtig har brug for. Jeg brugte det til at ignorere de genererede værdier. Vi sørger stadig for, at de findes, men vi er ligeglad med, hvad værdierne er. Bemærk, at jeg ikke engang tjekker typen. Vi har tillid til typeinferencer og standardværdier for at tage sig af det.

Et rør er et funktionelt værktøj, der giver dig mulighed for at skifte nogle inputværdier gennem en række funktioner, som hver tager output fra den forrige funktion og transformerer den på en eller anden måde. Jeg bruger lodash pipe fra `lodash / fp / pipe`, som er et alias for` lodash / flow`. Interessant kan 'pipe ()' selv oprettes med en reduktionsfunktion:

Jeg har en tendens til at bruge `pipe ()` meget i reduceringsfilerne såvel for at forenkle tilstandsovergange. Alle tilstandsovergange er i sidste ende dataflyt, der flytter fra en datarepresentation til den næste. Det er hvad 'pipe ()' er god til.

Bemærk, at handlingskaberen giver os mulighed for at tilsidesætte alle standardværdier, så vi kan videregive specifikke id'er og tidsstempler og teste for specifikke værdier.

Selector Tests

Til sidst tester vi tilstandsvælgerne og sørger for, at beregne værdier er korrekte, og at alt er som det skal:

Bemærk, at vi i denne test har brugt 'Array.prototype.reduce ()' til at reducere over et par eksempler på 'addChat ()' handlinger. En af de store ting ved Redux reducere er, at de bare er almindelige reduceringsfunktioner, hvilket betyder, at du kan gøre hvad som helst med dem, som du vil gøre med enhver anden reduktionsfunktion.

Vores 'forventede' værdi kontrollerer, at alle vores chatobjekter er i loggen, og at de nyligt aktive brugere er listet korrekt.

Ikke meget andet at sige om det.

Redux-regler

Hvis du bruger Redux korrekt, får du store fordele:

  • Fjern fejl i timeafhængighed
  • Aktivér deterministisk visning gengives
  • Aktivér deterministisk tilstandsgengivelse
  • Aktivér let fortryd / forny funktioner
  • Forenkling af fejlfinding
  • Bliv en tidsrejsende

Men for at noget af det kan fungere, skal du huske nogle regler:

  • Reduktionsmidler skal være rene funktioner
  • Nedsættere skal være den eneste kilde til sandhed for deres tilstand
  • Reducer-tilstand skal altid være serialiserbar
  • Reducer-tilstand bør ikke indeholde funktioner

Husk også:

  • Nogle apps har ikke brug for Redux
  • Brug konstanter til handlingstyper
  • Brug handlingskabere til at frakoble handlinglogik fra afsendelsesopkaldere
  • Brug standarder for ES6-parameter til selvbeskrivende underskrifter
  • Brug vælgere til beregnet tilstand og afkobling
  • Brug altid TDD!

God fornøjelse!

Er du klar til at niveauere dine Redux-færdigheder med DevAnywhere?

Lær avanceret funktionel programmering, React og Redux med 1: 1-mentorskab. Lifetime Access-medlemmer, tjek den funktionelle programmering og Redux-lektioner. Sørg for at se Shotgun-serien & køre haglgevær med mig, mens jeg bygger rigtige apps med React og Redux.

https://devanywhere.io/

Eric Elliott er forfatteren af ​​“Programmering JavaScript Applications” (O’Reilly) og medstifter af DevAnywhere.io. Han har bidraget til softwareoplevelser for Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC og topindspilningskunstnere, herunder Usher, Frank Ocean, Metallica og mange flere.

Han arbejder hvor som helst, han vil med den smukkeste kvinde i verden.