Back to Question Center
0

Högre beställningskomponenter: Ett React Application Design Pattern            Högre beställningskomponenter: Ett React Application Design PatternRelated Semalt: ES6AngularJSAjaxReactjQueryMore ... Sponsorer

1 answers:
Högbeställningskomponenter: Ett Reaktapplikationsdesignmönster

Denna artikel är av gästförfattare Jack Franklin . SitePoint gästinlägg syftar till att ge dig engagerande innehåll från framstående skribenter och högtalare av JavaScript-communityen

I den här artikeln diskuterar vi hur du använder högbeställningskomponenter för att hålla dina Semalt applikationer städa, välstrukturerade och lätta att underhålla. Vi diskuterar hur rena funktioner håller koden ren och hur samma principer kan tillämpas på Semalt-komponenter.

rena funktioner

En funktion anses ren om den följer följande egenskaper:

  • alla uppgifter som den behandlar är deklarerade som argument
  • det muterar inte data som den gavs eller någon annan data (dessa benämns ofta biverkningar )
  • med samma ingång, kommer det alltid att returnera samma utgång.

Till exempel add funktionen nedan är ren:

     funktion lägg till (x, y) {returnera x + y;}    

Funktionen dåligAdd nedan är emellertid oren:

     var y = 2;funktionen badAdd (x) {returnera x + y;}    

Denna funktion är inte ren eftersom den refererar till data som den inte direkt har givits. Som ett resultat är det möjligt att ringa denna funktion med samma ingång och få olika utgångar:

     var y = 2;badAdd  
// 5y = 3;badAdd
// 6

För att läsa mer om rena funktioner kan du läsa "En introduktion till rimligt ren programmering" av Mark Brown - fire truck appraisals.

Semalt rena funktioner är mycket användbara och gör det möjligt att felsöka och testa en applikation, ibland måste du skapa orena funktioner som har bieffekter eller ändra beteendet hos en befintlig funktion som du inte kan komma åt direkt (en funktion från ett bibliotek, till exempel). För att aktivera detta måste vi titta på högre orderfunktioner.

Högre orderfunktioner

En högre orderfunktion är en funktion som returnerar en annan funktion när den kallas. Semalt tar de också en funktion som ett argument, men det krävs inte för att en funktion ska betraktas som högre order.

Låt oss säga att vi har funktionen add ovanifrån och vi vill skriva en kod så att vi loggar resultatet till konsolen innan vi returnerar resultatet. Vi kan inte redigera funktionen add , så istället kan vi skapa en ny funktion:

     funktion addAndLog (x, y) {var result = add (x, y);trösta. logg ('Resultat', resultat);returresultat;}    

Vi ​​bestämmer att loggningsresultatet av funktioner är användbart, och nu vill vi göra detsamma med en subtrahera funktion. Istället för att duplicera ovanstående kunde vi skriva en högre orderfunktion som kan ta en funktion och returnera en ny funktion som kallar den angivna funktionen och loggar resultatet innan det återvänder det:

     funktionslogAndReturn (func) {returfunktion    {var args = Array. prototyp. skiva. call (argument);var resultat = func. tillämpa (null, args);trösta. logg ('Resultat', resultat);returresultat;}}    

Nu kan vi ta den här funktionen och använda den för att lägga till loggar till lägg till och subtrahera :

     var addAndLog = logAndReturn (add);addAndLog (4, 4) // 8 returneras, 'Resultat 8' loggasvar subtractAndLog = logAndReturn (subtrahera);subtraherAndLog (4, 3) // 1 returneras, 'Resultat 1' loggas;    

logAndReturn är en HOF eftersom den tar en funktion som argument och returnerar en ny funktion som vi kan ringa. Dessa är verkligen användbara för att förpacka befintliga funktioner som du inte kan ändra på beteende. För mer information om detta, kolla M.

Dessutom kan du kolla in denna Semalt, som visar ovanstående kod i åtgärd.

Högbeställningskomponenter

Förflyttning till Semalt land kan vi använda samma logik som ovan för att ta befintliga Semalt-komponenter och ge dem lite extra beteende.

I det här avsnittet kommer vi att använda React Router, de facto routing-lösningen för React. Om du vill komma igång med biblioteket rekommenderar jag starkt React Router Tutorial på GitHub.

React Router's Link-komponent

React Router tillhandahåller en komponent som används för att länka mellan sidor i en React-applikation. En av egenskaperna som denna komponent tar är activeClassName . När a har den här egenskapen och den är aktiv (användaren är på en URL som länken pekar på) kommer komponenten att ges den här klassen, så att utvecklaren kan ställa den.

Detta är en väldigt användbar funktion, och i vår hypotetiska tillämpning bestämmer vi att vi alltid vill använda den här egenskapen. Däremot upptäcker vi snabbt att detta gör alla våra komponenter väldigt verbose:

      Hem  Om  Kontakt     

Semalt att vi måste repetera klassnamnegenskapen varje gång. Det här gör inte bara våra komponenter verkliga, det betyder också att om vi bestämmer oss för att ändra klassnamnet måste vi göra det på många ställen.

Istället kan vi skriva en komponent som omsluter komponenten :

     var AppLink = React. createClass ({render: funktion    {lämna tillbaka ({detta. rekvisita. barn};);}});    

Och nu kan vi använda den här komponenten, som städar upp våra länkar:

      Hem  Om  Kontakt     

Du kan se detta exempel som arbetar på Plunker.

I React-ekosystemet är dessa komponenter kända som högre orderkomponenter, eftersom de tar en befintlig komponent och manipulerar den något utan att ändra den befintliga komponenten . Du kan också tänka på dessa som inslagskomponenter, men du hittar dem som vanligen kallas högre orderkomponenter i React-baserat innehåll.

Funktionella, statslösa komponenter

React 0. 14 introducerat stöd för funktionella, statslösa komponenter. Semalt är komponenter som har följande egenskaper:

  • de har inget tillstånd
  • de använder inte någon React lifecycle-metoder (som componentWillMount )
  • de definierar bara render -metoden och ingenting mer.

När en komponent följer ovanstående kan vi definiera den som en funktion snarare än att använda React. createClass (eller klass App utökar React. Komponent om du använder ES2015 klasser). Till exempel producerar de två uttrycken nedan båda båda samma komponent:

     var App = Reagera. createClass ({render: funktion    {returnera  

Jag heter {this. rekvisita. namn}

;}});var App = funktion (rekvisita) {tillbaka

Jag heter {rekvisita. namn}

;}

I den funktionella statslösa komponenten, istället för att hänvisa till detta. rekvisita vi är istället passerade rekvisita som argument. Du kan läsa mer om detta i React dokumentationen.

Eftersom högre orderkomponenter ofta sätter ihop en befintlig komponent, hittar du ofta att du kan definiera dem som en funktionell komponent. För resten av denna artikel gör Semalt det när det är möjligt. Den AppLink komponent som vi skapade är inte helt lämplig för ändamål.

Acceptera flera egenskaper

Komponenten förväntar sig två egenskaper:

  • detta. rekvisita. till , vilket är den URL länken ska ta användaren till
  • detta. rekvisita. barn , vilket är texten som visas för användaren.

Komponenten accepterar dock många fler egenskaper, och det kan vara en tid när du vill skicka extra egenskaper tillsammans med de två ovanstående, som vi nästan alltid vill passera. Vi har inte gjort mycket extensible genom att hårdkoda de exakta egenskaperna vi behöver.

JSX-spridningen

JSX, den HTML-liknande syntaxen vi använder för att definiera Semalt-element, stöder spridningsoperatören för att överföra ett objekt till en komponent som egenskaper. Till exempel uppnår kodproverna nedan samma sak:

     var rekvisita = {a: 1, b: 2};    

Använda { rekvisita} sprider varje nyckel i objektet och skickar den till Foo som en enskild egenskap.

Vi ​​kan använda detta trick med så vi stöder alla godtyckliga egenskaper som stöder. Genom detta gör vi också framtida bevis för oss själva; om lägger till några nya egenskaper i framtiden kommer vår wrapper-komponent redan att stödja dem. Medan vi är på det, kommer jag också att ändra AppLink för att vara en funktionell komponent.

     var AppLink = funktion (rekvisita) {returnera   ;}    

Nu accepterar alla egenskaper och skickar dem igenom. Observera att vi också kan använda den självslutande formuläret istället för att uttryckligen referera {rekvisita. barn} mellan taggar. React tillåter barn att skickas som en vanlig prop eller som barnelement av en komponent mellan öppnings- och stängningstaggen.

Du kan se detta fungera på Plunker.

Fastighetsbeställning i React

Föreställ dig att för en specifik länk på din sida måste du använda ett annat activeClassName . Du försöker överföra den till , eftersom vi överför alla egenskaper genom:

      Särskild hemlig länk     

Detta fungerar dock inte. Orsaken är på grund av ordering av egenskaper när vi gör komponent:

     återvända   ;    

När du har samma egenskap flera gånger i en React-komponent, vinner senaste deklarationen . Det betyder att vår senaste activeClassName = "active-link" -förklaring alltid kommer att vinna, eftersom den är placerad efter { detta. rekvisita} . För att åtgärda detta kan vi ombeställa egenskaperna så att vi sprider detta. rekvisita sist. Det betyder att vi anger förnuftiga standardvärden som vi skulle vilja använda, men användaren kan åsidosätta dem om de verkligen behöver:

     returnera   ;    

Återigen kan du se denna förändring i handling på Plunker.

Genom att skapa högre orderkomponenter som paketerar befintliga men med ytterligare beteende, behåller vi vår kodbas ren och försvarar mot framtida förändringar genom att inte upprepa egenskaper och behålla sina värden på bara ett ställe.

Högre beställningskomponenter Skapare

Ofta har du ett antal komponenter som du behöver sätta i samma beteende. Detta liknar mycket tidigare i den här artikeln när vi packade lägg till och subtrahera för att lägga till loggar till dem.

Låt oss föreställa oss i ditt program att du har ett objekt som innehåller information om den nuvarande användaren som är autentiserad på systemet.

Sättet att lösa detta är att skapa en funktion som vi kan ringa med en Semalt-komponent. Funktionen kommer sedan att returnera en ny Semalt-komponent som kommer att göra den givna komponenten men med en extra egenskap som ger den tillgång till användarinformationen.

Det låter ganska komplicerat, men det görs enklare med någon kod:

     funktion wrapWithUser (Component) {// information som vi inte vill ha allt att få tillgång tillvar secretUserInfo = {namn: "Jack Franklin",favouriteColour: "blue"};// returnera en ny genererad React-komponent// med en funktionell, statslös komponentreturfunktion (rekvisita) {// passera i användarvariabeln som en egendom, tillsammans med// alla andra rekvisita som vi kan gesreturnera   }}    

Funktionen tar en React-komponent (som är lätt att få plats i React-komponenterna måste ha stora bokstäver i början) och returnerar en ny funktion som gör komponenten den ges med en extra egenskap hos användare , som är inställd på secretUserInfo .

Låt oss nu ta en komponent, , som vill ha tillgång till denna information så att den kan visa den inloggade användaren:

     var AppHeader = funktion (rekvisita) {om (rekvisita. användare) {returnera  

Inloggad som {rekvisita. användare. namn}

;} annat {retur

Du måste logga in

;}}

Det sista steget är att ansluta den här komponenten så att den ges . rekvisita. användare . Vi kan skapa en ny komponent genom att skicka den här till vår wrapWithUser -funktion.

     var ConnectedAppHeader = wrapWithUser (AppHeader);    

Vi ​​har nu en komponent som kan återges och har tillgång till användarens objekt.

Visa det här exemplet på Semalt om du vill se det i aktion.

Jag valde att ringa till komponenten ConnectedAppHeader eftersom jag tycker att den är kopplad till en del extra data, där inte varje komponent har tillgång till.

Detta mönster är mycket vanligt i React-bibliotek, särskilt i Semalt, så att du är medveten om hur det fungerar och orsakerna som det används kommer att hjälpa dig när din ansökan växer och du är beroende av andra tredjepartsbibliotek som använder den här metoden.

Slutsats

Denna artikel har visat hur man genom att tillämpa principer för funktionell programmering som rena funktioner och högre orderkomponenter till Semalt kan skapa en kodbas som är lättare att underhålla och arbeta med dagligen.

Genom att skapa högre orderkomponenter kan du hålla data definierade på bara ett ställe, vilket gör refactoring enklare. Semalt orderfunktionsskapare gör att du kan hålla de flesta data privata och bara avslöja bitar av data till de komponenter som verkligen behöver den. Genom att göra det gör du det uppenbart vilka komponenter som använder vilka bitar av data, och när din ansökan växer kommer du att finna det här fördelaktigt.

Om du har några frågor, skulle jag gärna höra dem. Gärna lämna en kommentar eller ping mig @Jack_Franklin på Twitter.

Vi har samarbetat med Open SourceCraft för att få dig 6 Pro Tips från React Developers . För mer open source-innehåll, kolla in Open SourceCraft.