de Saurabh Misra
Conţinut
Acesta este motivul pentru care trebuie să legăm gestionarii de evenimente în Componentele clasei în React
În timp ce lucrați la React, trebuie să fi dat peste componente controlate și manipulatoare de evenimente. Trebuie să legăm aceste metode de instanța componentă folosind .bind()
în constructorul componentei noastre personalizate.
class Foo extends React.Component{
constructor( props ){
super( props );
this.handleClick = this.handleClick.bind(this);
}
handleClick(event){
// your event handling logic
}
render(){
return (
<button type="button"
onClick={this.handleClick}>
Click Me
</button>
);
}
}
ReactDOM.render(
<Foo />,
document.getElementById("app")
);
În acest articol, vom afla de ce trebuie să facem acest lucru.
Aș recomanda să citiți despre .bind()
aici dacă nu știi deja ce face.
Blame JavaScript, Not React
Ei bine, a da vina sună cam dur. Nu trebuie să facem acest lucru din cauza modului în care funcționează React sau din cauza JSX. Acest lucru se datorează modului în care this
legarea funcționează în JavaScript.
Să vedem ce se întâmplă dacă nu legăm metoda de gestionare a evenimentelor cu instanța componentă a acesteia:
class Foo extends React.Component{
constructor( props ){
super( props );
}
handleClick(event){
console.log(this); // 'this' is undefined
}
render(){
return (
<button type="button" onClick={this.handleClick}>
Click Me
</button>
);
}
}
ReactDOM.render(
<Foo />,
document.getElementById("app")
);
Dacă rulați acest cod, faceți clic pe butonul „Faceți clic pe mine” și verificați consola. Vei vedea undefined
tipărit pe consolă ca valoare a this
din interiorul metodei de gestionare a evenimentelor. handleClick()
metoda pare să aibă pierdut contextul său (instanță componentă) sau this
valoare.
Cum funcționează această legare în JavaScript
După cum am menționat, acest lucru se întâmplă din cauza modului this
legarea funcționează în JavaScript. Nu voi intra în multe detalii în această postare, dar aici este o resursă excelentă pentru a înțelege cum this
legarea funcționează în JavaScript.
Dar relevant pentru discuția noastră aici, valoarea this
în interiorul unei funcții depinde de modul în care este invocată acea funcție.
Legare implicită
function display(){
console.log(this); // 'this' will point to the global object
}
display();
Acesta este un apel simplu. Valoarea a this
în interiorul display()
metoda în acest caz este fereastra – sau obiectul global – în mod non-strict. În modul strict, this
valoarea este undefined
.
Legare implicită
var obj = {
name: 'Saurabh',
display: function(){
console.log(this.name); // 'this' points to obj
}
};
obj.display(); // Saurabh
Când numim o funcție în acest mod – precedată de un obiect contextual – this
valoare în interior display()
este setat sa obj
.
Dar când atribuim această referință de funcție altei variabile și invocăm funcția folosind această nouă referință de funcție, obținem o valoare diferită de this
interior display()
.
var name = "uh oh! global";
var outerDisplay = obj.display;
outerDisplay(); // uh oh! global
În exemplul de mai sus, când sunăm outerDisplay()
, nu specificăm un obiect context. Este un apel simplu de funcție fără obiect proprietar. În acest caz, valoarea lui this
interior display()
cade înapoi la legare implicită. Arată spre obiectul global sau undefined
dacă funcția invocată folosește modul strict.
Acest lucru se aplică în special în timp ce treceți astfel de funcții precum apelurile de apel către o altă funcție personalizată, o funcție de bibliotecă terță parte sau o funcție JavaScript încorporată, cum ar fi setTimeout
.
Considera setTimeout
definiție falsă așa cum se arată mai jos, apoi invocați-o.
// A dummy implementation of setTimeout
function setTimeout(callback, delay){
//wait for 'delay' milliseconds
callback();
}
setTimeout( obj.display, 1000 );
Ne putem da seama când apelăm setTimeout
, JavaScript atribuie intern obj.display
la argumentul său callback
.
callback = obj.display;
Această operațiune de atribuire, așa cum am văzut anterior, provoacă display()
funcție de a-și pierde contextul. Când acest apel invers este invocat în cele din urmă în interior setTimeout
, this
valoare în interior display()
cade înapoi la legare implicită.
var name = "uh oh! global";
setTimeout( obj.display, 1000 );
// uh oh! global
Legare dură explicită
Pentru a evita acest lucru, putem se leagă explicit în mod greu this
valoare pentru o funcție utilizând bind()
metodă.
var name = "uh oh! global";
obj.display = obj.display.bind(obj);
var outerDisplay = obj.display;
outerDisplay();
// Saurabh
Acum, când sunăm outerDisplay()
, valoarea a this
arata spre obj
interior display()
.
Chiar dacă trecem obj.display
ca apel invers, this
valoare în interior display()
va indica corect obj
.
Recreerea scenariului folosind numai JavaScript
La începutul acestui articol, am văzut acest lucru în componenta noastră React numită Foo
. Dacă nu am legat gestionarul de evenimente cu this
, valoarea sa în interiorul gestionarului de evenimente a fost setată ca undefined
.
După cum am menționat și explicat, acest lucru se datorează modului this
legarea funcționează în JavaScript și nu are legătură cu modul în care funcționează React. Deci, să eliminăm codul specific React și să construim un exemplu similar JavaScript pur pentru a simula acest comportament.
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
Nu simulăm evenimente și manipulatoare reale, ci folosim cod sinonim. După cum am observat în exemplul React Component, this
valoarea a fost undefined
deoarece contextul s-a pierdut după trecerea handlerului ca callback – sinonim cu o operațiune de atribuire. Aceasta este ceea ce observăm aici și în acest fragment JavaScript care nu reacționează.
“Așteptați un minut! Nu ar trebui this
valoare indică obiectul global, din moment ce rulăm acest lucru în mod non-strict în conformitate cu regulile legării implicite? ” s-ar putea să întrebi.
Nu. De-aceea:
Corpurile declarații de clasă și expresii de clasă sunt executate în modul strict, acesta este metoda constructor, static și prototip. Funcțiile Getter și Setter sunt executate în mod strict.
Puteți citi articolul complet aici.
Deci, pentru a preveni eroarea, trebuie să legăm this
valoare ca aceasta:
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
Nu trebuie să facem acest lucru în constructor și putem face acest lucru și în altă parte. Gandeste-te la asta:
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
Dar constructorul este cel mai optim și mai eficient loc pentru codificarea declarațiilor de legare a gestionarului de evenimente, având în vedere că aici are loc toată inițializarea.
De ce nu trebuie să legămthis’
pentru funcțiile Arrow?
Mai avem două modalități prin care putem defini gestionari de evenimente în interiorul unei componente React.
class Foo extends React.Component{
handleClick = () => {
console.log(this);
}
render(){
return (
<button type="button" onClick={this.handleClick}>
Click Me
</button>
);
}
}
ReactDOM.render(
<Foo />,
document.getElementById("app")
);
class Foo extends React.Component{
handleClick(event){
console.log(this);
}
render(){
return (
<button type="button" onClick={(e) => this.handleClick(e)}>
Click Me
</button>
);
}
}
ReactDOM.render(
<Foo />,
document.getElementById("app")
);
Ambele folosesc funcțiile săgeată introduse în ES6. Când se utilizează aceste alternative, gestionarul nostru de evenimente este deja legat automat de instanța componentă și nu este necesar să-l legăm în constructor.
Motivul este că, în cazul funcțiilor săgeată, this
este legat lexical. Aceasta înseamnă că folosește contextul funcției de închidere – sau global – ca scop this
valoare.
În cazul exemplului de sintaxă a câmpurilor de clasă publică, funcția săgeată este închisă în interiorul Foo
class – sau funcția constructor – deci contextul este instanța componentă, ceea ce dorim.
În cazul funcției săgeată ca exemplu de apel invers, funcția săgeată este închisă în interiorul render()
, care este invocată de React în contextul instanței componente. Acesta este motivul pentru care funcția săgeată va capta, de asemenea, același context și this
valoarea din interiorul acestuia va indica corect instanța componentă.
Pentru mai multe detalii referitoare la lexical this
obligatoriu, verificați această resursă excelentă.
Pentru a face scurtă o poveste lungă
În Componentele clasei din React, când trecem referința funcției de gestionare a evenimentelor ca un apel invers ca acesta
<button type="button" onClick={this.handleClick}>Click Me</button>
metoda de gestionare a evenimentelor își pierde implicit legat context. Când apare evenimentul și este invocat handler-ul, this
valoarea revine la legare implicită și este setat la undefined
, deoarece declarațiile de clasă și metodele de prototip rulează în mod strict.
Când legăm this
al gestionarului de evenimente către instanța componentă din constructor, îl putem transmite ca apel invers fără să ne îngrijorăm că își pierde contextul.
Funcțiile săgeată sunt scutite de acest comportament deoarece le folosesc lexical this
legare care le leagă automat de domeniul în care sunt definite.
#Acesta #este #motivul #pentru #care #trebuie #să #legăm #gestionarii #evenimente #în #Componentele #clasei #în #React
Acesta este motivul pentru care trebuie să legăm gestionarii de evenimente în Componentele clasei în React