Stateful vs Stateless Functional Components i React

React er et populært JavaScript-front-end-bibliotek for å bygge interaktive brukergrensesnitt. React har en forholdsvis lav lærekurve, noe som er en av grunnene til at det blir all oppmerksomhet i det siste. 

Selv om det er mange viktige begreper som skal dekkes, er komponenter ubestridelig hjertet og sjelen til React. Å ha en god forståelse av komponenter bør gjøre livet ditt enkelt som en React-utvikler. 

Forutsetninger

Denne opplæringen er beregnet for nybegynnere som har begynt å lære React og trenger en bedre oversikt over komponenter. Vi vil begynne med komponentens grunnleggende og deretter gå videre til mer utfordrende konsepter som komponentmønstre og når du skal bruke disse mønstrene. Ulike komponent klassifikasjoner har blitt dekket, for eksempel klasse vs funksjonelle komponenter, stateful vs stateless komponenter og container vs presentasjonskomponenter. 

Før jeg begynner, vil jeg introdusere deg til kodestykket som vi skal bruke i denne opplæringen. Det er en enkel teller bygget med React. Jeg vil referere tilbake til noen deler av dette eksemplet gjennom hele opplæringen. 

Så la oss komme i gang. 

Hva er komponenter?

Komponenter er selvbærende, uavhengige mikroenheter som beskriver en del av brukergrensesnittet ditt. En applikasjons brukergrensesnitt kan deles opp i mindre komponenter der hver komponent har sin egen kode, struktur og API. 

Facebook har for eksempel tusenvis av deler av funksjonalitet knyttet sammen når du ser på deres webapplikasjon. Her er et interessant faktum: Facebook består av 30.000 komponenter, og tallet vokser. Komponentarkitekturen lar deg tenke på hvert stykke i isolasjon. Hver komponent kan oppdatere alt i sitt omfang uten å være bekymret for hvordan det påvirker andre komponenter. 

Hvis vi tar Facebooks brukergrensesnitt som et eksempel, vil søkefeltet være en god kandidat for en komponent. Facebooks Newsfeed ville lage en annen komponent (eller en komponent som er vert for mange delkomponenter). Alle metodene og AJAX-anropene som er opptatt av søkefeltet, vil være innenfor denne komponenten.

Komponenter er også gjenbrukbare. Hvis du trenger samme komponent på flere steder, er det enkelt. Ved hjelp av JSX-syntaks kan du deklarere komponentene dine hvor du vil at de skal vises, og det er det. 

 
Nåværende telle: this.state.count
/ * Komponentgjenbrukbarhet i handling. * /

Rekvisitter og stat

Komponenter trenger data for å jobbe med. Det er to forskjellige måter at du kan kombinere komponenter og data: enten som Rekvisitter eller stat. rekvisitter og stat bestemme hva en komponent gjør og hvordan den oppfører seg. La oss starte med rekvisitter.

Rekvisitter

Hvis komponentene var enkle JavaScript-funksjoner, ville rekvisitter være funksjonsinngangen. Å gå etter den analogien, aksepterer en komponent en inngang (det vi kaller rekvisitter), prosesserer det, og gjør deretter noen JSX-kode.

Selv om dataene i rekvisitter er tilgjengelige for en komponent, er React filosofi at rekvisitter bør være uforanderlige og topp ned. Hva dette betyr er at en forelderskomponent kan passere på hvilken data den ønsker sine barn som rekvisitter, men barnekomponenten kan ikke endre rekvisitter. Så, hvis du prøver å redigere rekvisita som jeg gjorde under, vil du få "Kan ikke tilordne seg til skrivebeskyttet" TypeError.

const-knapp = (rekvisitter) => // rekvisitter er bare lesere props.count = 21; ...

Stat

Stat, derimot, er et objekt som eies av komponenten der den er erklært. Dens omfang er begrenset til dagens komponent. En komponent kan initialisere sin tilstand og oppdatere den når det er nødvendig. Tilstanden til foreldrekomponenten slutter som regel å være rekvisitter av barnekomponenten. Når staten er gått ut av dagens omfang, refererer vi til det som en prop.


Nå som vi kjenner komponentens grunnleggende, la oss ta en titt på grunnleggende klassifisering av komponenter.

Klassekomponenter kontra funksjonelle komponenter

En React-komponent kan være av to typer: enten en klassekomponent eller en funksjonell komponent. Forskjellen mellom de to er tydelig fra navnene sine. 

Funksjonelle komponenter

Funksjonelle komponenter er bare JavaScript-funksjoner. De tar inn et valgfritt innspill som, som jeg har nevnt tidligere, er det vi kaller rekvisitter.


Noen utviklere foretrekker å bruke de nye ES6-pilfunksjonene for å definere komponenter. Pilfunksjonene er mer kompakte og gir en kortfattet syntaks for å skrive funksjonsuttrykk. Ved å bruke en pilfunksjon kan vi hoppe over bruken av to søkeord, funksjon og komme tilbake, og et par krøllete parenteser. Med den nye syntaksen kan du definere en komponent i en enkelt linje som dette. 

const Hello = (name) => (
Hei, navn!
);

Klassekomponenter

Klassekomponenter tilbyr flere funksjoner, og med flere funksjoner kommer mer bagasje. Den primære grunnen til å velge klassekomponenter over funksjonelle komponenter er at de kan ha stat.

Statens = count: 1 syntaks er en del av funksjonen for offentlig klassefelt. Mer om dette nedenfor. 

Det er to måter du kan opprette en klassekomponent på. Den tradisjonelle måten er å bruke React.createClass (). ES6 introduserte en syntaks sukker som lar deg skrive klasser som strekker seg React.Component. Imidlertid er begge metodene ment å gjøre det samme. 

Klassekomponenter kan eksistere uten tilstand også. Her er et eksempel på en klassekomponent som aksepterer input-rekvisitter og gjør JSX.

klasse Hello utvider React.Component konstruktør (rekvisitter) super (rekvisitter);  gjengivelse () retur ( 
Hei rekvisitter
)

Vi definerer en konstruktormetode som aksepterer rekvisitter som input. Inne i konstruktøren, vi ringer super() å passere ned det som blir arvet fra foreldreklassen. Her er noen detaljer du kanskje har savnet.

Først er konstruktøren valgfri mens du definerer en komponent. I det ovennevnte tilfellet har komponenten ikke en tilstand, og konstruktøren ser ikke ut til å gjøre noe nyttig. this.props brukt inne i render () vil fungere uansett om konstruktøren er definert eller ikke. Men her er noe fra de offisielle doksene:

Klassekomponenter skal alltid ringe til grunnkonstruktøren med Rekvisitter.

Som en god praksis vil jeg anbefale å bruke konstruktøren til alle klassekomponenter.

For det andre, hvis du bruker en konstruktør, må du ringe super(). Dette er ikke valgfritt, og du får syntaxfeilen "Mangler super () ringer inn konstruktør" ellers. 

Og min siste poeng handler om bruken av super() vs. super (props). super (props) bør brukes hvis du skal ringe this.props inne i konstruktøren. Ellers bruker du super() alene er tilstrekkelig.

Stateful Components vs Stateless Components

Dette er en annen populær måte å klassifisere komponenter på. Og kriteriene for klassifisering er enkle: komponentene som har tilstand og komponentene som ikke gjør det. 

Statlige komponenter

Statlige komponenter er alltid klassekomponenter. Som tidligere nevnt har stateful komponenter en tilstand som blir initialisert i konstruktøren. 

// Her er et utdrag fra tellereksempelkonstruktøren (rekvisitter) super (rekvisitter); this.state = count: 0; 

Vi har opprettet et tilstandsobjekt og initialisert det med en telling på 0. Det er en alternativ syntaks som foreslås for å gjøre dette lettere kalt klassefelt. Det er ikke en del av ECMAScript-spesifikasjonen enda, men hvis du bruker en Babel-transpiler, bør denne syntaksen tråkke ut av esken.

klasse App utvider komponent / * // Ikke nødvendig lenger constructor () super (); this.state = count: 1 * / state = count: 1; handleCount (verdi) this.setState ((prevState) => (telle: prevState.count + verdi));  gjengivelse () // utelatt for korthet

Du kan unngå å bruke konstruktøren helt med denne nye syntaksen.

Vi kan nå få tilgang til staten i klassemetoder, inkludert render (). Hvis du skal bruke dem inni render () For å vise verdien av den nåværende tellingen, må du plassere den i krøllete parentes på følgende måte:

render () return (nåværende antall: this.state.count)

De dette søkeord her refererer til forekomsten av gjeldende komponent. 

Initialisering av staten er ikke nok - vi må kunne oppdatere staten for å skape et interaktivt program. Hvis du trodde dette ville fungere, nei, det vil det ikke.

// Feil måte handleCount (verdi) this.state.count = this.state.count + value; 

 Reaktorkomponenter er utstyrt med en metode kalt setState for oppdatering av tilstanden. setState aksepterer en gjenstand som inneholder den nye tilstanden til telle.

// Dette fungerer handleCount (verdi) this.setState (count: this.state.count + value); 

De setState () aksepterer et objekt som en inngang, og vi øker den forrige verdien av telling med 1, som fungerer som forventet. Det er imidlertid en fangst. Når det er flere setState-samtaler som leser en tidligere verdi av staten og skriver en ny verdi inn i det, kan vi ende opp med en løpevilkår. Det betyr at de endelige resultatene ikke stemmer overens med forventede verdier.

Her er et eksempel som bør gjøre det klart for deg. Prøv dette i codeandbox-utsnittet ovenfor.

// Hva er forventet produksjon? Prøv det i kode sandkassen. handleCount (verdi) this.setState (count: this.state.count + 100); this.setState (count: this.state.count + value); this.setState (count: this.state.count-100); 

Vi vil at setState skal øke antallet med 100, deretter oppdatere den med 1, og deretter fjerne den 100 som ble lagt til tidligere. Hvis setState utfører tilstandsovergangen i den aktuelle rekkefølgen, vil vi få forventet oppførsel. SetState er imidlertid asynkron, og flere setState-samtaler kan bli batchet sammen for bedre brukeropplevelse og ytelse. Så overkoden gir en oppførsel som er forskjellig fra det vi forventer.

Derfor, i stedet for direkte å sende et objekt, kan du passere i en oppdateringsfunksjon som har signaturen:

(prevState, rekvisitter) => stateChange 

prevState er en referanse til forrige tilstand og er garantert å være oppdatert. rekvisitter refererer til komponentens rekvisitter, og vi trenger ikke rekvisitter for å oppdatere staten her, så vi kan ignorere det. Derfor kan vi bruke den til å oppdatere tilstanden og unngå raseforholdet.

// Den riktige måten handleCount (verdi) this.setState ((prevState) => count: prevState.count +1); 

De setState () Rerenders komponenten, og du har en fungerende stateful komponent.

Statløse komponenter

Du kan bruke enten en funksjon eller en klasse for å lage statsløse komponenter. Men med mindre du trenger å bruke en livscykelkrok i komponentene dine, bør du gå til statløse funksjonelle komponenter. Det er mange fordeler hvis du bestemmer deg for å bruke statsløse funksjonelle komponenter her; de er enkle å skrive, forstå og teste, og du kan unngå dette søkeord helt og holdent. Men fra React v16 er det ingen fordeler ved å bruke statløse funksjonelle komponenter over klassekomponenter. 

Ulempen er at du ikke kan ha livssyklus kroker. Livscyklusmetoden ShouldComponentUpdate () brukes ofte til å optimalisere ytelsen og å kontrollere manuelt hva som blir gjengitt. Du kan ikke bruke det med funksjonelle komponenter ennå. Refs blir heller ikke støttet.

Containerkomponenter mot presentasjonskomponenter

Dette er et annet mønster som er veldig nyttig når du skriver komponenter. Fordelen med denne tilnærmingen er at adferdslogikken er skilt fra presentasjonslogikken.

Presentasjonskomponenter

Presentasjonskomponenter er kombinert med visningen eller hvordan ting ser ut. Disse komponentene aksepterer rekvisitter fra beholderens motpart og gir dem. Alt som har å gjøre med å beskrive brukergrensesnittet, bør gå her. 

Presentasjonskomponenter er gjenbrukbare og skal forbli frakoblet fra atferdslaget. En presentasjonskomponent mottar dataene og tilbakekallene utelukkende via rekvisitter, og når en hendelse oppstår, utføres en tilbakering til beholderkomponenten via rekvisitter som en knapp som blir trykket for å påkalle en hendelseshåndteringsmetode. 

Funksjonelle komponenter bør være ditt førstevalg for å skrive presentasjonskomponenter med mindre en stat er påkrevd. Hvis en presentasjonskomponent krever en stat, bør den være opptatt av brukergrensesnittet og ikke faktiske data. Presentasjonskomponenten samhandler ikke med Redux-butikken eller foretar API-anrop. 

Containerkomponenter

Containerkomponenter vil håndtere den adferdsmessige delen. En beholderkomponent forteller presentasjonskomponenten hva som skal gjengis ved hjelp av rekvisitter. Det bør ikke inneholde begrensede DOM-markeringer og stiler. Hvis du bruker Redux, inneholder en containerkomponent koden som sender en handling til en butikk. Alternativt er dette stedet du bør plassere API-anropene dine, og lagre resultatet i komponentens tilstand. 

Den vanlige strukturen er at det er en beholderkomponent øverst som overfører dataene til barnas presentasjonskomponenter som rekvisitter. Dette fungerer for mindre prosjekter; Men når prosjektet blir større og du har mange mellomliggende komponenter som bare aksepterer rekvisitter og sender dem videre til barnekomponenter, vil dette bli ubehagelig og vanskelig å vedlikeholde. Når dette skjer, er det bedre å lage en beholderkomponent som er unik for bladkomponenten, og dette vil lette byrden på mellomponentene.

Så hva er en ren komponent?

Du får høre termen ren komponent veldig ofte i React sirkler, og så er det React.PureComponent. Når du er ny for å reagere, kan alt dette høres litt forvirrende ut. En komponent sies å være ren hvis det er garantert å returnere det samme resultatet gitt de samme rekvisita og tilstand. En funksjonell komponent er et godt eksempel på en ren komponent fordi du vet hva som skal gjengis, gitt en inngang. 

const HelloWorld = (name) => ( 
'Hei $ name'
);

Klassekomponenter kan være rene også så lenge rekvisitter og tilstand er uforanderlige. Hvis du har en komponent med et "dypt" uforanderlig sett med rekvisitter og tilstand, har React API noe som heter PureComponent. React.PureComponent den er lik React.Component, men det implementerer ShouldComponentUpdate () Metoden litt annerledes. ShouldComponentUpdate () er påkalt før noe er gjengitt. Standard oppførsel er at den returnerer sant, slik at enhver endring i staten eller rekvisitter ragerer komponenten.

shouldComponentUpdate (nextProps, nextState) return true; 

Imidlertid utfører PureComponent en grunne sammenligning av objekter. Kort sammenligning betyr at du sammenligner objektets umiddelbare innhold i stedet for rekursivt å sammenligne alle nøkkel / verdi par av objektet. Så bare objektreferanser blir sammenlignet, og hvis staten / rekvisitter er muterte, virker dette kanskje ikke som ønsket. 

React.PureComponent brukes til å optimalisere ytelsen, og det er ingen grunn til at du bør vurdere å bruke det med mindre du opplever noen form for ytelsesproblem. 

Siste tanker

Stateless funksjonelle komponenter er mer elegante og er vanligvis et godt valg for å bygge presentasjonskomponenter. Fordi de bare fungerer, vil du ikke ha det vanskelig å skrive og forstå dem, og i tillegg er de døde lett å teste. 

Det skal bemerkes at statsløse funksjonelle komponenter ikke har overhånd når det gjelder optimalisering og ytelse fordi de ikke har en ShouldComponentUpdate () kroken. Dette kan endres i fremtidige versjoner av React, hvor funksjonelle komponenter kan optimaliseres for bedre ytelse. Men hvis du ikke er kritisk for ytelsen, bør du holde deg til funksjonelle komponenter for visning / presentasjon og stateful klassekomponenter for beholderen.

Forhåpentligvis har denne opplæringen gitt deg en oversikt over komponentbasert arkitektur og ulike komponentmønstre i React på høyt nivå. Hva er dine tanker om dette? Del dem gjennom kommentarene.

I løpet av de siste par årene har React vokst i popularitet. Faktisk har vi en rekke elementer i Envato Market som er tilgjengelige for kjøp, gjennomgang, implementering og så videre. Hvis du leter etter flere ressurser rundt React, ikke nøl med å sjekke dem ut.