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, thisbindingen 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 undefinedudskrevet til konsollen som værdien af thisindefra begivenhedshåndteringsmetoden. Den handleClick()metode synes at have mistet sin kontekst (komponenteksempel) eller thisværdi.

Hvordan 'denne' binding fungerer i JavaScript

Som jeg nævnte, sker dette på grund af den måde, thisbinding fungerer i JavaScript. Jeg vil ikke gå i detaljer i dette indlæg, men her er en stor ressource til at forstå, hvordan thisbindingen fungerer i JavaScript.

Men relevant for vores diskussion her thisafhæ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 thisinde i display()metoden er i dette tilfælde vinduet - eller det globale - objekt i ikke-streng tilstand. I streng tilstand er thisvæ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 thisvæ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 thisindeni 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 thisindvendigt display()tilbage til standardbinding . Det peger på det globale objekt, eller undefinedhvis 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 setTimeoutdummy-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.displaysit 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 thisvæ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 thisvæ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(), thispeger værdien af objindvendigt display().

Selv hvis vi videresender obj.displaysom tilbagekald, vil thisvæ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 thisbinding 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 thisværdien, undefinedda 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 thisvæ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 thisvæ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, thisfalder værdien tilbage til standardbinding og er indstillet til undefined, da klassedeklarationer og prototypemetoder kører i streng tilstand.

Når vi binder thisbegivenhedshå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 thisbinding som automatisk binder dem til anvendelsesområdet de er defineret i.