Componente funcționale statale vs. fără stat în reacție

React este o populară bibliotecă front-end JavaScript pentru construirea de interfețe utilizator interactive. React are o curbă relativ superficială de învățare, care este unul dintre motivele pentru care a primit toată atenția în ultima vreme. 

Deși există multe concepte importante care trebuie acoperite, componentele sunt incontestabil inima și sufletul React. Având o bună înțelegere a componentelor ar trebui să vă facă viața mai ușoară ca dezvoltator React. 

Cerințe preliminare

Acest tutorial este destinat începătorilor care au început să învețe React și au nevoie de o imagine mai bună a componentelor. Vom începe cu elementele de bază ale componentelor și apoi vom trece la concepte mai dificile, cum ar fi modelele componentelor și când vom folosi aceste modele. Au fost acoperite diferite clasificări ale componentelor, cum ar fi componente de clasă vs. funcționale, componente statale vs. apatrid și componente container / prezentare. 

Înainte de a începe, vreau să vă prezint fragmentul de cod pe care îl vom folosi în acest tutorial. Este un contor simplu construit cu React. Voi face referire la câteva părți ale acestui exemplu în tutorial. 

Deci sa începem. 

Care sunt componentele?

Componentele sunt micro-entități autonome și independente care descriu o parte din interfața dvs. utilizator. Aplicația UI poate fi împărțită în componente mai mici, în care fiecare componentă are propriul cod, structură și API. 

Facebook, de exemplu, are mii de bucăți de funcționalitate interfațate împreună când vizualizați aplicația lor web. Iată un fapt interesant: Facebook cuprinde 30.000 de componente, iar numărul crește. Arhitectura componente vă permite să vă gândiți la fiecare piesă în mod izolat. Fiecare componentă poate actualiza totul în domeniul său de aplicare fără a fi preocupat de modul în care afectează alte componente. 

Dacă luăm UI-ul Facebook ca exemplu, bara de căutare ar fi un bun candidat pentru o componentă. Facebook Newsfeed-ul ar face o altă componentă (sau o componentă care găzduiește mai multe subcomponente). Toate metodele și apelurile AJAX referitoare la bara de căutare ar fi în interiorul acelei componente.

Componentele sunt de asemenea reutilizabile. Dacă aveți nevoie de aceeași componentă în mai multe locuri, este ușor. Cu ajutorul sintaxei JSX, puteți declara componentele dvs. oriunde doriți să apară, și asta este. 

 
Număr curent: this.state.count
/ * Reutilizabilitatea componentei în acțiune. * /

Props și stat

Componentele au nevoie de date pentru a lucra cu. Există două moduri diferite în care puteți combina componente și date: fie ca recuzită sau stat. elemente de recuzită și stat determină ceea ce face o componentă și cum se comportă. Să începem cu recuzită.

recuzită

Dacă componentele ar fi simple funcții JavaScript, atunci recuzita ar fi funcția de intrare. Trecând prin acea analogie, o componentă acceptă o intrare (ceea ce numim recuzită), o procesează și apoi redă unele coduri JSX.

Deși datele din recuzită sunt accesibile unei componente, filozofia React este că recuzita ar trebui să fie imuabilă și de sus în jos. Ce înseamnă acest lucru este faptul că o componentă părinte poate transmite oricare ar fi datele pe care le dorește copiilor săi ca elemente de recuzită, dar componenta copil nu își poate modifica recuzita. Deci, dacă încercați să editați elemente de recuză așa cum am făcut mai jos, veți obține opțiunea "Nu se poate atribui doar pentru citire" TypeError.

const Button = (recuzită) => // props sunt doar citite props.count = 21; ...

Stat

Statul, pe de altă parte, este un obiect deținut de componenta în care este declarată. Domeniul său de aplicare este limitat la componenta curentă. O componentă își poate inițializa starea și o poate actualiza ori de câte ori este necesar. Starea componentei părinte se termină, de obicei, în funcție de componenta copilului. Atunci când statul este trecut din domeniul de aplicare actual, ne referim la acesta ca o propunere.


Acum, când cunoaștem elementele de bază ale componentelor, să aruncăm o privire la clasificarea de bază a componentelor.

Componentele clasei vs. componentele funcționale

O componentă React poate fi de două tipuri: fie o componentă de clasă, fie o componentă funcțională. Diferența dintre cele două este evidentă din numele lor. 

Componente funcționale

Elementele funcționale sunt doar funcții JavaScript. Ei iau o intrare opțională care, așa cum am menționat mai devreme, este ceea ce numim recuzită.


Unii dezvoltatori preferă să utilizeze noile funcții de săgeată ES6 pentru definirea componentelor. Funcțiile cu săgeți sunt mai compacte și oferă o sintaxă concisă pentru scrierea expresiilor de funcții. Folosind o funcție de săgeată, putem să nu folosim două cuvinte cheie, funcţie și întoarcere, și o pereche de paranteze curbate. Cu noua sintaxă, puteți defini o componentă într-o singură linie ca aceasta. 

const Hello = (nume) => (
Bună ziua, name!
);

Componente de clasă

Componentele de clasă oferă mai multe caracteristici, iar cu mai multe caracteristici sunt mai multe bagaje. Motivul principal pentru alegerea componentelor de clasă pe componentele funcționale este că acestea pot avea stat.

Sintaxa state = count: 1 face parte din funcția câmpurilor de clasă publică. Mai multe despre aceasta mai jos. 

Există două moduri în care puteți crea o componentă de clasă. Modul tradițional este de a utiliza React.createClass (). ES6 a introdus un sintaxă de zahăr care vă permite să scrieți clase care se extind React.Component. Cu toate acestea, ambele metode sunt menite să facă același lucru. 

Componentele de clasă pot exista fără stat. Iată un exemplu de componentă de clasă care acceptă un recuzit de intrare și face JSX.

class Hello extinde React.Component constructor (recuzită) super (recuzită);  render () return ( 
Bună ziua props
)

Definim o metodă constructor care acceptă elemente de recuză ca intrări. În interiorul constructorului, sunăm super() pentru a transfera ceea ce este moștenit din clasa părintească. Iată câteva detalii pe care le-ați pierdut.

În primul rând, constructorul este opțional în timp ce definește o componentă. În cazul de mai sus, componenta nu are o stare și constructorul nu pare să facă nimic util. this.props utilizat în interiorul face() va funcționa indiferent dacă constructorul este definit sau nu. Cu toate acestea, iată ceva din documentele oficiale:

Elementele de clasă ar trebui să apeleze întotdeauna constructorul de bază cu recuzită.

Ca o bună practică, vă recomand să folosiți constructorul pentru toate componentele de clasă.

În al doilea rând, dacă utilizați un constructor, trebuie să sunați super(). Acest lucru nu este opțional și veți primi eroarea de sintaxă "Lipsesc apelul de tip super () constructor" in caz contrar. 

Și ultimul meu punct este despre utilizarea super() vs. super-(popi). super-(popi) ar trebui să fie utilizate dacă intenționați să apelați this.props în interiorul constructorului. Altfel, folosind super() singur este suficient.

Componentele statului față de componentele fără stat

Acesta este un alt mod popular de clasificare a componentelor. Criteriile pentru clasificare sunt simple: componentele care au starea și componentele care nu au. 

Componente statale

Componentele de stat sunt întotdeauna componente de clasă. Așa cum am menționat anterior, componentele statale au o stare care devine inițializată în constructor. 

// Iată un extras din constructorul exemplu de contra (recuzită) super (recuzită); this.state = număr: 0; 

Am creat un obiect de stare și l-am inițializat cu un număr de 0. Există o sintaxă alternativă propusă pentru a face acest câmp de clasă mai ușor numit. Nu este încă o parte a specificației ECMAScript, dar dacă utilizați un transpilator Babel, această sintaxă ar trebui să funcționeze din cutie.

clasa App extinde Component / * // Nu mai este nevoie de constructor () super (); this.state = count: 1 * / state = count: 1; handleCount (valoare) this.setState ((prevState) => (count: prevState.count + value));  render () // omis pentru scurtă durată

Puteți evita utilizarea constructorului cu această sintaxă nouă.

Acum putem accesa statul în cadrul metodelor de clasă, inclusiv face(). Dacă îi vei folosi înăuntru face() pentru a afișa valoarea numărului curent, trebuie să îl plasați în paranteze curbate după cum urmează:

render () return (Numar curent: this.state.count)

acest cuvântul cheie aici se referă la instanța componentei curente. 

Inițializarea stării nu este suficientă - trebuie să putem actualiza starea pentru a crea o aplicație interactivă. Dacă credeai că acest lucru ar funcționa, nu, nu.

// Metoda greșită handleCount (valoare) this.state.count = această sumă statală + valoare; 

 Componentele reactive sunt echipate cu o metodă numită setState pentru actualizarea stării. setState acceptă un obiect care conține noua stare a numara.

// Aceasta funcționează handleCount (valoare) this.setState (count: this.state.count + value); 

setState () acceptă un obiect ca intrare și noi creștem valoarea precedentă a numărului cu 1, care funcționează conform așteptărilor. Cu toate acestea, există o captură. Când există mai multe setări ale apelurilor care citesc o valoare anterioară a stării și scriu o nouă valoare în ea, s-ar putea să ajungem la o condiție de cursă. Ce inseamna asta este ca rezultatele finale nu se vor potrivi cu valorile asteptate.

Iată un exemplu care ar trebui să vă clarifice pentru dvs. Încercați acest lucru în paragraful codesandbox de mai sus.

// Care este rezultatul așteptat? Încercați-l în nisipul de cod. handleCount (valoare) this.setState (count: this.state.count + 100); this.setState (count: this.state.count + value); this.setState (count: this.state.count-100); 

Vrem ca setState să crească contorul cu 100, apoi să îl actualizeze cu 1, apoi să elimine acel 100 care a fost adăugat mai devreme. Dacă setState efectuează tranziția de stat în ordinea reală, vom obține comportamentul așteptat. Cu toate acestea, setState este asincron și mai multe setState de apeluri pot fi difuzate împreună pentru o mai bună experiență și performanță UI. Deci, codul de mai sus dă un comportament diferit de ceea ce ne așteptăm.

Prin urmare, în loc să treci direct un obiect, poți trece într-o funcție updater care are semnătura:

(prevState, recuzită) => stareChange 

prevState este o referință la starea anterioară și este garantată a fi actualizată. props se referă la elemente de recuzită ale componentei și nu avem nevoie de elemente de recuzită pentru a actualiza starea aici, astfel încât să putem ignora acest lucru. Prin urmare, îl putem folosi pentru actualizarea stării și pentru a evita condiția de rasă.

// Calea potrivită handleCount (valoare) this.setState ((prevState) => count: prevState.count +1); 

setState () reeditează componenta și aveți o componentă de lucru statală.

Componente fără stat

Puteți utiliza fie o funcție, fie o clasă pentru a crea componente apatride. Dar, dacă nu aveți nevoie să utilizați un cârlig de viață în componentele dvs., ar trebui să mergeți pentru componente funcționale fără stat. Există o mulțime de beneficii dacă vă decideți să utilizați aici componente funcționale fără stat; acestea sunt ușor de scris, de înțeles și de testat, și puteți evita acest cuvinte cheie cu totul. Cu toate acestea, începând cu React v16, nu există beneficii de performanță prin utilizarea componentelor funcționale fără statură peste componentele de clasă. 

Dezavantajul este că nu puteți avea cârlige pe durata ciclului de viață. Metoda ciclului de viață ShouldComponentUpdate () este adesea folosit pentru a optimiza performanța și pentru a controla manual ceea ce se repetă. Nu poți folosi încă componentele funcționale. Referințele nu sunt, de asemenea, acceptate.

Componente de containere vs. componente de prezentare

Acesta este un alt model care este foarte util în timp ce scrieți componente. Beneficiul acestei abordări este că logica comportamentului este separată de logica prezentării.

Componentele de prezentare

Componentele de prezentare sunt cuplate cu vizualizarea sau cum arată lucrurile. Aceste componente acceptă elemente de recuzită de la omologul lor container și le conferă. Tot ce are de-a face cu descrierea UI ar trebui să meargă aici. 

Componentele de prezentare sunt reutilizabile și trebuie să rămână decuplate de la stratul de comportament. O componentă de prezentare primește datele și callback-urile exclusiv prin recuzită și atunci când apare un eveniment, cum ar fi un buton apăsat, efectuează un apel invers la componenta containerului prin recuzită pentru a invoca o metodă de manipulare a evenimentelor. 

Componentele funcționale ar trebui să fie prima alegere pentru scrierea componentelor de prezentare, cu excepția cazului în care este necesară o stare. Dacă o componentă de prezentare necesită o stare, aceasta ar trebui să fie preocupată de starea UI și nu de date reale. Componenta de prezentare nu interacționează cu magazinul Redux sau face apeluri API. 

Componente pentru containere

Componentele containerului se vor ocupa de partea comportamentală. O componentă a containerului spune componenta de prezentare ce ar trebui să fie redat folosind elemente de recuzită. Nu ar trebui să conțină marcaje și stiluri DOM limitate. Dacă utilizați Redux, o componentă a containerului conține codul care trimite o acțiune către un magazin. Alternativ, acesta este locul unde trebuie să plasați apelurile API și să stocați rezultatul în starea componentei. 

Structura obișnuită este aceea că există o componentă de container în partea superioară care transmite datele în componentele de prezentare copil ca elemente de recuzită. Aceasta funcționează pentru proiecte mai mici; totuși, atunci când proiectul devine mai mare și aveți o mulțime de componente intermediare care acceptă doar elemente de recuzită și le transmit componentelor copilului, acest lucru va deveni urât și greu de întreținut. Când se întâmplă acest lucru, este mai bine să creați o componentă a containerului unică pentru componenta de frunză, ceea ce va ușura sarcina asupra componentelor intermediare.

Deci, ce este o compoziție pură?

Veți ajunge să auziți termenul de componentă pură foarte des în cercurile React, iar apoi nu există React.PureComponent. Când ești nou la React, toate astea ar putea părea puțin confuz. Se spune că o componentă este pură dacă se garantează că va reveni la același rezultat dat fiind aceleași elemente de recuzită și stare. O componentă funcțională este un bun exemplu de componentă pură deoarece, dat fiind o intrare, știi ce va fi redat. 

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

Componentele clasei pot fi pure și atâta timp cât elementele de recuzită și statutul lor sunt imuabile. Dacă aveți o componentă cu un set de imaterializabil "iminent" de recuzită și stare, React API are ceva numit PureComponent. React.PureComponent este similar cu React.Component, dar implementează ShouldComponentUpdate () metoda un pic diferit. ShouldComponentUpdate () este invocată înainte ca ceva să fie reintrodus. Comportamentul implicit este că se întoarce la adevărat, astfel încât orice modificare a stării sau a elementelor de recuzită să redea elementul.

shouldComponentUpdate (nextProps, nextState) return true; 

Cu toate acestea, cu PureComponent, efectuează o comparație superficială a obiectelor. Comparație de dimensiuni reduse înseamnă că comparați conținutul imediat al obiectelor în loc să comparați în mod recursiv toate perechile cheie / valoare ale obiectului. Deci, doar referințele obiectului sunt comparate, iar dacă starea / recuzita sunt mutate, acest lucru s-ar putea să nu funcționeze așa cum a fost intenționat. 

React.PureComponent este utilizat pentru optimizarea performanței și nu există niciun motiv pentru care ar trebui să luați în considerare utilizarea acesteia dacă nu întâmpinați un fel de problemă de performanță. 

Gândurile finale

Componentele funcționale fără stat sunt mai elegante și de obicei reprezintă o alegere bună pentru construirea componentelor de prezentare. Deoarece acestea sunt doar funcții, nu veți avea dificultăți de a scrie și de a le înțelege și, în plus, ele sunt moarte ușor de testat. 

Trebuie remarcat faptul că componentele funcționale apatride nu au mâna superioară în ceea ce privește optimizarea și performanța, deoarece nu au ShouldComponentUpdate () cârlig. Acest lucru s-ar putea schimba în viitoarele versiuni ale React, în care componentele funcționale ar putea fi optimizate pentru o performanță mai bună. Cu toate acestea, dacă nu sunteți critic față de performanță, trebuie să vă lipiți de componentele funcționale pentru elementele de vizualizare / prezentare și clasă de stare pentru container.

Sperăm că acest tutorial vă oferă o imagine de ansamblu la nivel înalt a arhitecturii bazate pe componente și a diferitelor modele de componente din React. Care sunt gândurile tale despre asta? Trimiteți-le prin comentarii.

În ultimii ani, React a crescut în popularitate. De fapt, avem o serie de elemente din Envato Market care sunt disponibile pentru cumpărare, revizuire, implementare și așa mai departe. Dacă sunteți în căutarea unor resurse suplimentare în jurul React, nu ezitați să le verificați.

Cod