Seetõttu peame Reacti klassi komponentide jaoks sündmuste käitlejad siduma

Reactiga töötades peate olema kokku puutunud juhitavate komponentide ja sündmuste käitlejatega. Peame need meetodid siduma komponendi eksemplariga, kasutades .bind()meie kohandatud komponendi konstruktorit.

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

Selles artiklis uurime, miks me peame seda tegema.

Soovitaksin lugeda .bind()siit, kui te ei tea veel, mida see teeb.

Süüdista JavaScripti, mitte reageeri

Noh, süüdistuse esitamine kõlab natuke karmilt. See pole midagi, mida me peame tegema React'i toimimisviisi või JSX-i tõttu. Selle põhjuseks on thisköitmise JavaScripti toimimine.

Vaatame, mis juhtub, kui me ei seo sündmuste käitleja meetodit selle komponendi eksemplariga:

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

Selle koodi käivitamisel klõpsake nuppu „Klõpsake mind“ ja kontrollige oma konsooli. Näete undefinedkonsooli trükitud väärtust thissündmuste käitleja meetodi seestpoolt. handleClick()Meetod näib olevat kadunud konteksti (komponent näiteks) või thisväärtus.

Kuidas see sidumine JavaScriptis töötab

Nagu ma mainisin, juhtub see thisköitmise JavaScripti toimimise tõttu. Ma ei hakka selles postituses palju üksikasju laskma, kuid siin on suurepärane ressurss, et mõista, kuidas thisköitmine JavaScripti abil töötab.

Kuid meie siinse arutelu seisukohalt thison funktsiooni sisemuse väärtus sõltuv sellest, kuidas seda funktsiooni kasutatakse.

Vaikimisi sidumine

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

See on tavaline funktsioonikõne. Meetodi thissees olev väärtus on display()sel juhul aken - või globaalne - objekt mitte-ranges režiimis. Ranges režiimis on thisväärtus undefined.

Kaudne sidumine

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

Kui kutsume funktsiooni sel viisil - millele eelneb kontekstiobjekt - seatakse thissees display()olevaks väärtuseks obj.

Kuid kui omistame selle funktsiooni viite mõnele muule muutujale ja kutsume funktsiooni selle uue funktsiooniviite abil, saame thisseestpoolt erineva väärtuse display().

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

Ülaltoodud näites outerDisplay()ei määra me helistades konteksti objekti. See on tavalise funktsiooni väljakutse ilma omanikuobjektita. Sellisel juhul langeb thisseestpoolt display()tagasi vaikesidumine . See osutab globaalsele objektile või undefinedkui kasutatav funktsioon kasutab ranget režiimi.

See kehtib eriti siis, kui sellised funktsioonid nagu tagasihelistamised edastatakse teisele kohandatud funktsioonile, kolmanda osapoole teegi funktsioonile või sisseehitatud JavaScripti funktsioonile setTimeout.

Mõelge setTimeoutnäiva definitsioonile, nagu allpool näidatud, ja kasutage seda.

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

Saame aru, et helistades setTimeoutmäärab JavaScript obj.displayoma argumendi sisemiselt callback.

callback = obj.display;

See määramisoperatsioon, nagu me oleme varem näinud, põhjustab display()funktsiooni konteksti kaotuse. Kui seda tagasihelistamist lõpuks sisse kutsutakse setTimeout, langeb sees olev thisväärtus display()tagasi vaikimisi sidumisele .

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

Selgesõnaline kõva köitmine

Selle vältimiseks saame selgelt raske sidudathis väärtust funktsiooni abil bind()meetodit.

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

Nüüd, kui me helistame outerDisplay(), näitab thispunktide väärtus objsisemusse display().

Isegi kui me obj.displayhelistame tagasihelistusena tagasi, osutab thissees olev väärtus display()õigesti obj.

Stsenaariumi taastamine ainult JavaScripti abil

Selle artikli alguses nägime seda meie komponendis nimega React Foo. Kui me ei seostanud sündmusetöötlejat this, määrati selle väärtuseks sündmuste käitleja sees undefined.

Nagu ma mainisin ja selgitasin, on see tingitud sellest, kuidas thisköitmine JavaScripti abil toimib, ja pole seotud Reactiga. Nii eemaldame React-spetsiifilise koodi ja koostame selle käitumise simuleerimiseks sarnase puhta JavaScripti näite.

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

Me ei simuleeri tegelikke sündmusi ja käitlejaid, vaid hoopis sünonüümset koodi. Nagu React Componenti näites täheldasime, oli thisväärtus selline, undefinednagu kontekst kadus pärast käitleja tagasihelistamisena möödumist - see oli määramisoperatsiooni sünonüüm. Seda jälgime siin ka selles Reageerimata JavaScripti jupis.

"Oota hetk! Kas thisväärtus ei peaks näitama globaalset objekti, kuna me töötame seda mittesiduvas režiimis vastavalt vaikesidumise reeglitele? " võite küsida.

Ei. Sellepärast:

Klassideklaratsioonide ja klassiväljendite kehad täidetakse ranges režiimis, see tähendab konstruktori, staatilise ja prototüübi meetodid. Getteri ja setteri funktsioonid täidetakse ranges režiimis.

Täispikka artiklit saate lugeda siit.

Nii et vea vältimiseks peame siduma thisväärtuse järgmiselt:

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

Me ei pea seda konstruktoris tegema ja saame seda teha ka kusagil mujal. Mõelge sellele:

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

sündmuste käitleja meetod kaotab kaudselt seotud konteksti. Kui sündmus toimub ja käitleja kutsutakse, thislangeb väärtus tagasi vaikimisi sidumisele ja seatakse väärtusele undefined, kuna klassi deklaratsioonid ja prototüüpmeetodid töötavad ranges režiimis.

Kui seome thissündmuste käitleja konstruktori komponendi eksemplariga, võime selle edastada tagasihelistamiseta, muretsemata selle konteksti kaotamise pärast.

Noolefunktsioonid on sellest käitumisest vabastatud, kuna nad kasutavad leksikaalset thissidumist, mis seob need automaatselt ulatusega, milles need on määratletud.