Siksi meidän on sidottava tapahtumankäsittelijät Reactin luokan osiin

Reactin parissa työskennellessäsi on oltava törmännyt hallittuihin komponentteihin ja tapahtumankäsittelijöihin. Meidän on sidottava nämä menetelmät komponentti-ilmentymään käyttämällä .bind()mukautetun komponentin konstruktorissa.

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") );

Tässä artikkelissa aiomme selvittää, miksi meidän on tehtävä tämä.

Suosittelen lukemaan .bind()tästä, jos et vielä tiedä, mitä se tekee.

Syyttää JavaScriptiä, ei reagoi

No, syyttäminen kuulostaa hieman ankaralta. Tätä ei tarvitse tehdä Reactin toiminnan tai JSX: n takia. Tämä johtuu siitä, miten thissidonta toimii JavaScriptissä.

Katsotaanpa, mitä tapahtuu, jos emme sido tapahtumankäsittelymenetelmää sen komponentti-ilmentymällä:

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") );

Jos suoritat tämän koodin, napsauta "Click Me" -painiketta ja tarkista konsoli. Tulet näkemään undefinedkonsolille tulostettuna thistapahtumankäsittelijän menetelmän arvon. handleClick()Menetelmä näyttää menettänyt sen yhteydessä (komponentti esimerkiksi) tai thisarvo.

Kuinka tämä sidonta toimii JavaScriptissä

Kuten mainitsin, tämä tapahtuu johtuen siitä, miten thissidonta toimii JavaScriptissä. En käsittele paljon yksityiskohtia tässä viestissä, mutta tässä on hieno resurssi ymmärtää, miten thissidonta toimii JavaScriptissä.

Mutta tässä keskustelussamme thison merkitystä funktion sisällä, riippuen siitä, miten kyseistä toimintoa käytetään.

Oletussidonta

function display(){ console.log(this); // 'this' will point to the global object } display(); 

Tämä on tavallinen toimintakutsu. Menetelmän thissisäpuolen arvo on display()tässä tapauksessa ikkuna - tai globaali - objekti ei-tiukassa tilassa. Tiukassa tilassa thisarvo on undefined.

Epäsuora sitoutuminen

var obj = { name: 'Saurabh', display: function(){ console.log(this.name); // 'this' points to obj } }; obj.display(); // Saurabh 

Kun kutsumme funktiota tällä tavalla - jota edeltää kontekstiobjekti -, thissisällä oleva arvo display()asetetaan arvoon obj.

Mutta kun osoitamme tämän funktioviitteen jollekin muulle muuttujalle ja kutsumme funktion käyttämään tätä uutta funktioviitettä, saamme eri arvon thissisältä display().

var name = "uh oh! global"; var outerDisplay = obj.display; outerDisplay(); // uh oh! global

Yllä olevassa esimerkissä, kun soitamme outerDisplay(), emme määritä kontekstiobjektia. Se on tavallinen toimintokutsu ilman omistajaobjektia. Tällöin thissisäpuolen arvo display()laskee takaisin oletussidontaan . Se osoittaa globaaliin objektiin tai undefinedjos käytettävä toiminto käyttää tiukkaa tilaa.

Tämä on erityisen hyödyllistä, kun sellaiset toiminnot kuin soittopyynnöt välitetään toiselle mukautetulle toiminnolle, kolmannen osapuolen kirjastofunktiolle tai sisäänrakennetulle JavaScript-toiminnolle, kuten setTimeout.

Harkitse setTimeoutnuken määritelmää alla esitetyllä tavalla ja käytä sitten sitä.

// A dummy implementation of setTimeout function setTimeout(callback, delay){ //wait for 'delay' milliseconds callback(); } setTimeout( obj.display, 1000 );

Voimme selvittää, että kun soitamme setTimeout, JavaScript määrittää sisäisesti obj.displayargumenttinsa callback.

callback = obj.display;

Tämä määritystoiminto, kuten olemme nähneet aiemmin, saa display()toiminnon menettämään kontekstinsa. Kun tämä takaisinsoitto lopulta kutsutaan sisälle setTimeout, sisällä oleva thisarvo display()laskee takaisin oletusarvoiseen sidontaan .

var name = "uh oh! global"; setTimeout( obj.display, 1000 ); // uh oh! global

Selkeä kova sitova

Tämän välttämiseksi voimme nimenomaan kovaa sitoathis arvon käyttamällä bind()menetelmällä.

var name = "uh oh! global"; obj.display = obj.display.bind(obj); var outerDisplay = obj.display; outerDisplay(); // Saurabh

Nyt, kun soitamme outerDisplay(), thispisteiden arvo objsisälle display().

Vaikka obj.displayohittaisimme soittopyynnön, thissisällä oleva arvo display()osoittaa oikein obj.

Skenaarion luominen uudelleen vain JavaScriptiä käyttämällä

In the beginning of this article, we saw this in our React component called Foo . If we did not bind the event handler with this , its value inside the event handler was set as undefined.

As I mentioned and explained, this is because of the way this binding works in JavaScript and not related to how React works. So let’s remove the React-specific code and construct a similar pure JavaScript example to simulate this behavior.

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

We are not simulating actual events and handlers, but instead we are using synonymous code. As we observed in the React Component example, the this value was undefined as the context was lost after passing the handler as a callback — synonymous with an assignment operation. This is what we observe here in this non-React JavaScript snippet as well.

"Odota hetki! Eikö thisarvon pitäisi osoittaa globaalia objektia, koska suoritamme tätä ei-tiukassa tilassa oletussidonnan sääntöjen mukaisesti? " saatat kysyä.

Ei. Siksi:

Luokkailmoitusten ja lausekkeiden rungot suoritetaan tiukassa tilassa, ts. Konstruktori-, staattinen- ja prototyyppimenetelmät. Getter- ja setter-toiminnot suoritetaan tiukassa tilassa.

Voit lukea koko artikkelin täältä.

Joten virheen estämiseksi meidän on sidottava thisarvo seuraavasti:

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

Meidän ei tarvitse tehdä tätä konstruktorissa, ja voimme tehdä sen myös muualla. Harkitse tätä:

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

tapahtumankäsittelymenetelmä menettää implisiittisesti sidotun kontekstin. Kun tapahtuma tapahtuu ja käsittelijä kutsutaan, thisarvo laskee takaisin oletus sidontaan ja asetetaan arvoon undefined, kun luokan ilmoitukset ja prototyyppimenetelmät suoritetaan tiukassa tilassa.

Kun sidomme thistapahtumakäsittelijän komponentin ilmentymään konstruktorissa, voimme välittää sen takaisinsoittona huolimatta siitä, että se menettää kontekstinsa.

Nuolitoiminnot on vapautettu tästä käyttäytymisestä, koska ne käyttävät leksikaalista thissidontaa, joka sitoo ne automaattisesti määriteltyyn laajuuteen.