Dette er grunden til, at vi skal binde begivenhedshåndterere i klasse komponenter i React

Mens du arbejder på React, skal du være stødt på kontrollerede komponenter og eventhåndterere. Vi er nødt til at binde disse metoder til komponentinstansen ved hjælp .bind()
af vores brugerdefinerede komponents konstruktør.
class Foo extends React.Component{ constructor( props ){ super( props ); this.handleClick = this.handleClick.bind(this); } handleClick(event){ // your event handling logic } render(){ return ( Click Me ); } } ReactDOM.render( , document.getElementById("app") );
I denne artikel skal vi finde ud af, hvorfor vi har brug for at gøre dette.
Jeg vil anbefale at læse om .bind()
her, hvis du ikke allerede ved, hvad det gør.
Beskyld JavaScript, reager ikke
Nå, at lægge skylden lyder lidt hårdt. Dette er ikke noget, vi skal gøre på grund af den måde, React fungerer på, eller på grund af JSX. Dette skyldes den måde, this
bindingen fungerer i JavaScript.
Lad os se, hvad der sker, hvis vi ikke binder begivenhedshåndteringsmetoden med dens komponentforekomst:
class Foo extends React.Component{ constructor( props ){ super( props ); } handleClick(event){ console.log(this); // 'this' is undefined } render(){ return ( Click Me ); } } ReactDOM.render( , document.getElementById("app") );
Hvis du kører denne kode, skal du klikke på knappen "Klik på mig" og kontrollere din konsol. Du vil se undefined
udskrevet til konsollen som værdien af this
indefra begivenhedshåndteringsmetoden. Den handleClick()
metode synes at have mistet sin kontekst (komponenteksempel) eller this
værdi.
Hvordan 'denne' binding fungerer i JavaScript
Som jeg nævnte, sker dette på grund af den måde, this
binding fungerer i JavaScript. Jeg vil ikke gå i detaljer i dette indlæg, men her er en stor ressource til at forstå, hvordan this
bindingen fungerer i JavaScript.
Men relevant for vores diskussion her this
afhænger værdien af inde i en funktion af, hvordan denne funktion påberåbes.
Standardbinding
function display(){ console.log(this); // 'this' will point to the global object } display();
Dette er et almindeligt funktionsopkald. Værdien af this
inde i display()
metoden er i dette tilfælde vinduet - eller det globale - objekt i ikke-streng tilstand. I streng tilstand er this
værdien undefined
.
Implicit binding
var obj = { name: 'Saurabh', display: function(){ console.log(this.name); // 'this' points to obj } }; obj.display(); // Saurabh
Når vi kalder en funktion på denne måde - forud for et kontekstobjekt - indstilles this
værdien inden display()
for obj
.
Men når vi tildeler denne funktionsreference til en anden variabel og påkalder funktionen ved hjælp af denne nye funktionsreference, får vi en anden værdi this
indeni display()
.
var name = "uh oh! global"; var outerDisplay = obj.display; outerDisplay(); // uh oh! global
I ovenstående eksempel outerDisplay()
angiver vi ikke et kontekstobjekt , når vi ringer . Det er et almindeligt funktionsopkald uden et ejerobjekt. I dette tilfælde falder værdien af this
indvendigt display()
tilbage til standardbinding . Det peger på det globale objekt, eller undefined
hvis den funktion, der påberåbes, bruger streng tilstand.
Dette er især relevant, når du sender sådanne funktioner som tilbagekald til en anden brugerdefineret funktion, en tredjeparts biblioteksfunktion eller en indbygget JavaScript-funktion som setTimeout
.
Overvej setTimeout
dummy-definitionen som vist nedenfor, og påkald den derefter.
// A dummy implementation of setTimeout function setTimeout(callback, delay){ //wait for 'delay' milliseconds callback(); } setTimeout( obj.display, 1000 );
Vi kan finde ud af, at når vi ringer setTimeout
, tildeles JavaScript internt obj.display
sit argument callback
.
callback = obj.display;
Denne tildelingshandling, som vi har set før, får display()
funktionen til at miste sin kontekst. Når denne tilbagekaldelse til sidst påberåbes inde setTimeout
, falder this
værdien indeni display()
tilbage til standardbinding .
var name = "uh oh! global"; setTimeout( obj.display, 1000 ); // uh oh! global
Eksplicit hård binding
For at undgå dette, kan vi eksplicit hårdt binde den this
værdi til en funktion ved hjælp af bind()
metoden.
var name = "uh oh! global"; obj.display = obj.display.bind(obj); var outerDisplay = obj.display; outerDisplay(); // Saurabh
Nu når vi ringer outerDisplay()
, this
peger værdien af obj
indvendigt display()
.
Selv hvis vi videresender obj.display
som tilbagekald, vil this
værdien indeni display()
korrekt pege på obj
.
Genskaber scenariet ved kun at bruge JavaScript
I begyndelsen af denne artikel så vi dette i vores React-komponent kaldet Foo
. Hvis vi ikke bindte begivenhedshåndteringen med this
, blev dens værdi inde i begivenhedshåndtereren indstillet som undefined
.
Som jeg nævnte og forklarede, skyldes det, hvordan this
binding fungerer i JavaScript, og ikke relateret til, hvordan React fungerer. Så lad os fjerne den React-specifikke kode og konstruere et lignende rent JavaScript-eksempel for at simulere denne adfærd.
class Foo { constructor(name){ this.name = name } display(){ console.log(this.name); } } var foo = new Foo('Saurabh'); foo.display(); // Saurabh // The assignment operation below simulates loss of context // similar to passing the handler as a callback in the actual // React Component var display = foo.display; display(); // TypeError: this is undefined
Vi simulerer ikke faktiske begivenheder og håndterere, men i stedet bruger vi synonym kode. Som vi observerede i React Component-eksemplet, var this
værdien, undefined
da konteksten blev tabt, efter at passeren blev sendt som tilbagekald - synonymt med en tildelingshandling. Dette er, hvad vi observerer her i dette JavaScript-uddrag, der ikke er React.
"Vent et øjeblik! Bør ikke this
værdien pege på det globale objekt, da vi kører dette i ikke-streng tilstand i henhold til reglerne for standardbinding? ” spørger du måske.
Nej. Dette er grunden til:
Kropperne af klassedeklarationer og klasseudtryk udføres i streng tilstand, dvs. konstruktør-, statiske og prototype-metoder. Getter- og setterfunktioner udføres i streng tilstand.Du kan læse hele artiklen her.
Så for at forhindre fejlen skal vi binde this
værdien sådan:
class Foo { constructor(name){ this.name = name this.display = this.display.bind(this); } display(){ console.log(this.name); } } var foo = new Foo('Saurabh'); foo.display(); // Saurabh var display = foo.display; display(); // Saurabh
Vi behøver ikke at gøre dette i konstruktøren, og vi kan også gøre dette et andet sted. Overvej dette:
class Foo { constructor(name){ this.name = name; } display(){ console.log(this.name); } } var foo = new Foo('Saurabh'); foo.display = foo.display.bind(foo); foo.display(); // Saurabh var display = foo.display; display(); // Saurabh
But the constructor is the most optimal and efficient place to code our event handler bind statements, considering that this is where all the initialization takes place.
Why don’t we need to bind ‘this’
for Arrow functions?
We have two more ways we can define event handlers inside a React component.
- Public Class Fields Syntax(Experimental)
class Foo extends React.Component{ handleClick = () => { console.log(this); } render(){ return ( Click Me ); } } ReactDOM.render( , document.getElementById("app") );
- Arrow function in the callback
class Foo extends React.Component{ handleClick(event){ console.log(this); } render(){ return ( this.handleClick(e)}> Click Me ); } } ReactDOM.render( , document.getElementById("app") );
Both of these use the arrow functions introduced in ES6. When using these alternatives, our event handler is already automatically bound to the component instance, and we do not need to bind it in the constructor.
The reason is that in the case of arrow functions, this
is bound lexically. This means that it uses the context of the enclosing function — or global — scope as its this
value.
In the case of the public class fields syntax example, the arrow function is enclosed inside the Foo
class — or constructor function — so the context is the component instance, which is what we want.
In the case of the arrow function as callback example, the arrow function is enclosed inside the render()
method, which is invoked by React in the context of the component instance. This is why the arrow function will also capture this same context, and the this
value inside it will properly point to the component instance.
For more details regarding lexical this
binding, check out this excellent resource.
To make a long story short
In Class Components in React, when we pass the event handler function reference as a callback like this
Click Me
begivenhedshåndteringsmetoden mister sin implicit bundet kontekst. Når begivenheden finder sted, og handleren påberåbes, this
falder værdien tilbage til standardbinding og er indstillet til undefined
, da klassedeklarationer og prototypemetoder kører i streng tilstand.
Når vi binder this
begivenhedshåndteringen til komponentforekomsten i konstruktøren, kan vi videregive det som et tilbagekald uden at bekymre os om, at det mister sin kontekst.
Arrow funktioner er undtaget fra denne adfærd, fordi de bruger leksikale this
binding som automatisk binder dem til anvendelsesområdet de er defineret i.