Stateful vs. Stateless Functional Components in React

React is een populaire JavaScript-front-endbibliotheek voor het bouwen van interactieve gebruikersinterfaces. React heeft een relatief ondiepe leercurve, wat een van de redenen is waarom het de laatste tijd alle aandacht krijgt. 

Hoewel er veel belangrijke concepten moeten worden behandeld, zijn componenten onmiskenbaar het hart en de ziel van React. Een goed begrip van componenten zou je leven als React-ontwikkelaar gemakkelijk moeten maken. 

voorwaarden

Deze tutorial is bedoeld voor beginners die zijn begonnen met het leren van React en een beter overzicht van de componenten nodig hebben. We zullen beginnen met de basics van de componenten en vervolgens doorgaan naar meer uitdagende concepten zoals componentpatronen en wanneer die patronen moeten worden gebruikt. Er zijn verschillende componentclassificaties behandeld, zoals klasse vs. functionele componenten, stateful vs. stateless componenten en container versus presentatiecomponenten. 

Voordat ik aan de slag ga, wil ik u laten kennismaken met het codefragment dat we in deze zelfstudie zullen gebruiken. Het is een eenvoudige teller gebouwd met React. Ik zal tijdens de tutorial teruggaan naar enkele delen van dit voorbeeld. 

Dus laten we beginnen. 

Wat zijn componenten?

Componenten zijn zelfvoorzienende, onafhankelijke micro-entiteiten die een deel van uw gebruikersinterface beschrijven. De gebruikersinterface van een toepassing kan worden opgesplitst in kleinere componenten, waarbij elke component zijn eigen code, structuur en API heeft. 

Facebook heeft bijvoorbeeld duizenden stukjes functionaliteit aan elkaar gekoppeld wanneer je hun webapplicatie bekijkt. Hier is een interessant feit: Facebook bestaat uit 30.000 componenten en het aantal groeit. Met de componentarchitectuur kun je elk stuk afzonderlijk bekijken. Elk onderdeel kan alles in zijn scope bijwerken zonder zich zorgen te maken over de invloed op andere componenten. 

Als we de gebruikersinterface van Facebook als voorbeeld nemen, zou de zoekbalk een goede kandidaat zijn voor een component. Facebook's Newsfeed zou een andere component (of een component die vele subcomponenten host) maken. Alle methoden en AJAX-aanroepen met betrekking tot de zoekbalk zouden zich binnen die component bevinden.

Componenten zijn ook herbruikbaar. Als u dezelfde component op meerdere plaatsen nodig heeft, is dat eenvoudig. Met behulp van de JSX-syntaxis kunt u uw componenten declareren waar u ze ook wilt laten verschijnen, en dat is alles. 

 
Huidige telling: this.state.count
/ * Component-herbruikbaarheid in actie. * /

Props en State

Componenten hebben gegevens nodig om mee te werken. Er zijn twee verschillende manieren waarop u componenten en gegevens kunt combineren: één van beide rekwisieten of staat. rekwisieten en staat bepalen wat een component oplevert en hoe het zich gedraagt. Laten we beginnen met rekwisieten.

rekwisieten

Als componenten eenvoudige JavaScript-functies waren, dan zouden props de functie-invoer zijn. Gaand door die analogie, accepteert een component een input (wat we props noemen), verwerkt het en geeft dan een aantal JSX-code weer.

Hoewel de gegevens in rekwisieten toegankelijk zijn voor een component, is de React-filosofie dat rekwisieten onveranderlijk en van bovenaf moeten zijn. Wat dit betekent is dat een oudercomponent gegevens die het wil aan zijn kinderen kan doorgeven als rekwisieten, maar de onderliggende component kan de rekwisieten niet wijzigen. Dus als je de props probeert te bewerken zoals ik hieronder deed, krijg je de "Can not assign to read-only" TypeError.

const Button = (props) => // props zijn read only props.count = 21; ...

Staat

Staat, aan de andere kant, is een object dat eigendom is van het onderdeel waar het wordt gedeclareerd. Het toepassingsgebied is beperkt tot het huidige onderdeel. Een component kan de status ervan initialiseren en deze indien nodig bijwerken. De status van de oudercomponent eindigt meestal als rekwisieten van de onderliggende component. Wanneer de status buiten de huidige scope wordt doorgegeven, noemen we het een prop.


Nu we de basiscomponenten kennen, laten we eens kijken naar de basisclassificatie van componenten.

Klassecomponenten versus functionele componenten

Een component React kan uit twee typen bestaan: een klassecomponent of een functionele component. Het verschil tussen de twee blijkt uit hun namen. 

Functionele componenten

Functionele componenten zijn slechts JavaScript-functies. Ze nemen een optionele input op die, zoals ik eerder heb gezegd, props is.


Sommige ontwikkelaars geven er de voorkeur aan de nieuwe ES6-pijlfuncties te gebruiken voor het definiëren van componenten. Pijlfuncties zijn compacter en bieden een beknopte syntaxis voor het schrijven van functie-uitdrukkingen. Door een pijlfunctie te gebruiken, kunnen we het gebruik van twee sleutelwoorden overslaan, functie en terugkeer, en een paar accolades. Met de nieuwe syntaxis kunt u een component in een enkele regel als deze definiëren. 

const Hello = (name) => (
Hallo, naam!
);

Klasse-componenten

Klasseonderdelen bieden meer functies en met meer functies komt meer bagage. De primaire reden om klassecomponenten te kiezen boven functionele componenten is dat ze kunnen hebben staat.

De syntaxis state = count: 1 maakt deel uit van de functie public class fields. Meer hierover hieronder. 

Er zijn twee manieren om een ​​klassecomponent te maken. De traditionele manier is om te gebruiken React.createClass (). ES6 heeft een syntaxisuiker geïntroduceerd waarmee je klassen kunt schrijven die worden uitgebreid React.Component. Beide methoden zijn echter bedoeld om hetzelfde te doen. 

Klasseonderdelen kunnen ook zonder status bestaan. Hier is een voorbeeld van een klassecomponent die invoerproperties accepteert en JSX rendert.

class Hello extends React.Component constructor (props) super (rekwisieten);  render () retourneren ( 
Hallo props
)

We definiëren een constructormethode die rekwisieten als invoer accepteert. In de constructor bellen we super() om door te geven wat er wordt geërfd van de ouderklasse. Hier zijn enkele details die u mogelijk hebt gemist.

Ten eerste is de constructor optioneel tijdens het definiëren van een component. In het bovenstaande geval heeft de component geen status en lijkt de constructor niets nuttigs te doen. this.props gebruikt binnen de render () zal werken, ongeacht of de constructor is gedefinieerd of niet. Hier is echter iets van de officiële documenten:

Klasseonderdelen moeten altijd de basisconstructor bellen met rekwisieten.

Als best practice zal ik aanbevelen om de constructor te gebruiken voor alle klassecomponenten.

Ten tweede, als je een constructor gebruikt, moet je bellen super(). Dit is niet optioneel en u krijgt de syntaxisfout "Ontbrekende super () oproep in constructeur" anders-. 

En mijn laatste punt gaat over het gebruik van super() vs. super (props). super (props) moet worden gebruikt als je gaat bellen this.props in de constructor. Anders, met behulp van super() alleen is voldoende.

Stateful Components vs. Stateless Components

Dit is een andere populaire manier om componenten te classificeren. En de criteria voor de classificatie zijn eenvoudig: de componenten met status en de componenten die dat niet doen. 

Stateful Components

Statische componenten zijn altijd klassecomponenten. Zoals eerder vermeld, hebben stateful componenten een status die wordt geïnitialiseerd in de constructor. 

// Hier is een uittreksel van de contra-voorbeeldconstructor (rekwisieten) super (rekwisieten); this.state = count: 0; 

We hebben een statusobject gemaakt en het geïnitialiseerd met een telling van 0. Er is een alternatieve syntaxis voorgesteld om dit gemakkelijker klasse-velden te maken. Het is nog geen onderdeel van de ECMAScript-specificatie, maar als u een Babel-transponder gebruikt, moet deze syntaxis uit de doos werken.

class-app breidt component uit / * // niet meer nodig constructor () super (); this.state = count: 1 * / state = count: 1; handleCount (waarde) this.setState ((prevState) => (count: prevState.count + value));  render () // weggelaten voor beknoptheid

U kunt voorkomen dat u de constructor helemaal gebruikt met deze nieuwe syntaxis.

We kunnen nu toegang krijgen tot de staat binnen de klassemethoden, waaronder render (). Als je ze binnen gaat gebruiken render () om de waarde van de huidige telling weer te geven, moet je deze als volgt tussen accolades plaatsen:

render () return (Huidige telling: this.state.count)

De deze sleutelwoord verwijst hier naar het exemplaar van het huidige onderdeel. 

Het initialiseren van de status is niet voldoende - we moeten in staat zijn om de status bij te werken om een ​​interactieve applicatie te maken. Als je dacht dat dit zou werken, nee, dat doet het niet.

// Wrong way handleCount (value) this.state.count = this.state.count + value; 

 Reactieve componenten zijn uitgerust met een methode genaamd setState om de status bij te werken. setState accepteert een object dat de nieuwe staat van de bevat tellen.

// Dit werkt handleCount (waarde) this.setState (count: this.state.count + value)); 

De setstate () accepteert een object als invoer en we verhogen de vorige waarde van telling met 1, die werkt zoals verwacht. Er is echter een vangst. Wanneer er meerdere setState-oproepen zijn die een vorige waarde van de status lezen en een nieuwe waarde erin schrijven, kunnen we eindigen met een race-situatie. Wat dat betekent is dat de eindresultaten niet overeenkomen met de verwachte waarden.

Hier is een voorbeeld dat het voor u duidelijk moet maken. Probeer dit in het codesandbox-fragment hierboven.

// Wat is de verwachte output? Probeer het in de codesandbox. handleCount (waarde) this.setState (count: this.state.count + 100); this.setState (tel: this.state.count + value); this.setState (count: this.state.count-100); 

We willen dat de setState de telling met 100 verhoogt, vervolgens update met 1 en vervolgens die 100 verwijdert die eerder is toegevoegd. Als setState de statusovergang in de daadwerkelijke volgorde uitvoert, krijgen we het verwachte gedrag. SetState is echter asynchroon en meerdere setState-aanroepen kunnen samen worden gecombineerd voor betere gebruikerservaring en -prestaties. Dus de bovenstaande code levert een gedrag op dat verschilt van wat we verwachten.

Daarom kunt u in plaats van direct een object door te geven een updatefunctie doorgeven die de handtekening heeft:

(prevState, props) => stateChange 

prevState is een verwijzing naar de vorige staat en is gegarandeerd up-to-date. rekwisieten verwijst naar de rekwisieten van het onderdeel en we hebben geen rekwisieten nodig om de staat hier bij te werken, dus we kunnen dit negeren. Vandaar dat we het kunnen gebruiken voor het updaten van de toestand en het vermijden van de racetoestand.

// De juiste manier handleCount (waarde) this.setState ((prevState) => count: prevState.count +1); 

De setstate () rendeers de component, en je hebt een werkende stateful component.

Staatloze componenten

U kunt een functie of een klasse gebruiken voor het maken van stateloze componenten. Maar tenzij u een levenscyclushaak in uw componenten moet gebruiken, zou u voor staatloze functionele componenten moeten gaan. Er zijn veel voordelen als u besluit om stateloze functionele componenten hier te gebruiken; ze zijn gemakkelijk te schrijven, te begrijpen en te testen, en u kunt het voorkomen deze sleutelwoord helemaal. Vanaf React v16 zijn er echter geen prestatievoordelen bij het gebruik van stateloze functionele componenten ten opzichte van klassecomponenten. 

Het nadeel is dat je levenscyclushaken niet kunt hebben. De levenscyclusmethode ShouldComponentUpdate () wordt vaak gebruikt om de prestaties te optimaliseren en om handmatig te controleren wat er wordt herhaald. Je kunt dat nog niet gebruiken met functionele componenten. Refs worden ook niet ondersteund.

Containercomponenten versus presentatiecomponenten

Dit is een ander patroon dat erg handig is bij het schrijven van componenten. Het voordeel van deze aanpak is dat de gedragslogica gescheiden is van de presentatielogica.

Presentatiecomponenten

Presentatiecomponenten worden gekoppeld aan het beeld of hoe de dingen eruit zien. Deze componenten accepteren rekwisieten van hun container-tegenhanger en maken ze. Alles wat te maken heeft met het beschrijven van de gebruikersinterface zou hierheen moeten gaan. 

Presentatiecomponenten zijn herbruikbaar en moeten ontkoppeld blijven van de gedragslaag. Een presentatiecomponent ontvangt de gegevens en callbacks uitsluitend via rekwisieten en wanneer een gebeurtenis plaatsvindt, zoals een knop die wordt ingedrukt, voert het een callback naar de containercomponent uit via rekwisieten om een ​​gebeurtenisafhandelingsmethode aan te roepen. 

Functionele componenten moeten de eerste keuze zijn voor het schrijven van presentaties, tenzij een voorwaarde vereist is. Als een presentatieonderdeel een status vereist, moet deze zich bezighouden met de UI-status en niet met feitelijke gegevens. De presentatiecomponent heeft geen interactie met de Redux-winkel of maakt API-oproepen. 

Containercomponenten

Containeronderdelen zullen het gedragsgedeelte behandelen. Een containercomponent vertelt de presentatiecomponent wat moet worden weergegeven met behulp van rekwisieten. Het mag geen beperkte DOM-markeringen en stijlen bevatten. Als u Redux gebruikt, bevat een containercomponent de code die een actie naar een winkel verzendt. Als alternatief is dit de plaats waar u uw API-aanroepen plaatst en het resultaat in de status van de component opslaat. 

De gebruikelijke structuur is dat er bovenaan een containercomponent is die de gegevens doorgeeft aan de presenterende componenten van het kind als rekwisieten. Dit werkt voor kleinere projecten; Wanneer het project echter groter wordt en je veel intermediaire componenten hebt die alleen rekwisieten accepteren en doorgeven aan onderliggende componenten, wordt dit vervelende en moeilijk te onderhouden. Wanneer dit gebeurt, is het beter om een ​​containercomponent te maken die uniek is voor de bladcomponent, en dit zal de last op de tussencomponenten verlichten.

Dus wat is een PureComponent?

Je zult de term pure component heel vaak horen in React-kringen, en dan is er dat wel React.PureComponent. Als je nieuw bent bij React, klinkt dit allemaal een beetje verwarrend. Er wordt gezegd dat een component zuiver is als het gegarandeerd hetzelfde resultaat oplevert, met dezelfde attributen en status. Een functionele component is een goed voorbeeld van een pure component omdat u, gegeven een invoer, weet wat er wordt weergegeven. 

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

Klassecomponenten kunnen puur zijn, zolang hun rekwisieten en toestand onveranderlijk zijn. Als u een component hebt met een 'diepe' onveranderlijke set attributen en status, heeft React API iets genaamd PureComponent. React.PureComponent is gelijkaardig aan React.Component, maar het implementeert de ShouldComponentUpdate () methode een beetje anders. ShouldComponentUpdate () wordt aangeroepen voordat er iets wordt herhaald. Het standaardgedrag is dat het true retourneert, zodat elke wijziging in de staat of de props het component retourneert.

shouldComponentUpdate (nextProps, nextState) return true; 

Met PureComponent voert het echter een ondiepe vergelijking van objecten uit. Ondiepe vergelijking betekent dat u de onmiddellijke inhoud van de objecten vergelijkt in plaats van alle sleutel / waarde-paren van het object recursief met elkaar te vergelijken. Dus alleen de objectreferenties worden vergeleken en als de staat / rekwisieten gemuteerd zijn, werkt dit misschien niet zoals bedoeld. 

React.PureComponent wordt gebruikt voor het optimaliseren van de prestaties en er is geen reden waarom u zou moeten overwegen om het te gebruiken, tenzij u een prestatieprobleem ondervindt. 

Laatste gedachten

Stateless functionele componenten zijn eleganter en zijn meestal een goede keuze voor het bouwen van de presentatieve componenten. Omdat het gewoon functies zijn, zult u het niet moeilijk vinden om ze te schrijven en te begrijpen, en bovendien zijn ze doodeenvoudig te testen. 

Opgemerkt moet worden dat stateloze functionele componenten niet de overhand hebben op het gebied van optimalisatie en prestaties omdat ze geen a hebben ShouldComponentUpdate () haak. Dit zou kunnen veranderen in toekomstige versies van React, waar functionele componenten kunnen worden geoptimaliseerd voor betere prestaties. Als u echter niet kritisch bent over de prestaties, moet u zich houden aan functionele componenten voor de weergave / presentatie en stateful klasse-componenten voor de container.

Hopelijk heeft deze tutorial je een overzicht op hoog niveau gegeven van de op componenten gebaseerde architectuur en verschillende componentpatronen in React. Wat vind je hiervan? Deel ze via de opmerkingen.

In de afgelopen paar jaar is React in populariteit gegroeid. In feite hebben we een aantal items in Envato Market die beschikbaar zijn voor aankoop, beoordeling, implementatie enzovoort. Als u op zoek bent naar extra bronnen rond React, aarzel dan niet om ze te bekijken.