How does event handlers with arrow functions achieve context binding

限于喜欢 提交于 2019-11-27 09:08:36

As the MDN docs states

An arrow function does not have its own this; the this value of the enclosing execution context is used

So you would think of

onClick={(e) => this.goToStore(e)}

as being an anonymous function which can be written as

    (e) => { 
         return this.goToStore(e) 
    }

Now here in this anonymous function this refers to the lexical context of the render function which in turn refers to the React Class instance.

Now

Context is most often determined by how a function is invoked. When a function is called as a method of an object, this is set to the object the method is called on:

var obj = {
    foo: function() {
        return this;   
    }
};

obj.foo() === obj; // true

The same principle applies when invoking a function with the new operator to create an instance of an object. When invoked in this manner, the value of this within the scope of the function will be set to the newly created instance:

function foo() {
    alert(this);
}

foo() // window
new foo() // foo

When called as an unbound function, this will default to the global context or window object in the browser.

So here since the function is called like this.goToStore() this inside it will refer to the context of React component.

However when you write onClick={this.goToStore}, the function is not executed but a reference of it is assigned to the onClick function, which then later invokes it, causing this to be undefined inside the function as the function runs on the context of the window object.

Now even though onClick={(e) => this.goToStore(e)} works, whenever render is called a new function instance is created. In your case its easy to avoid, just by created the function goToStore using arrow function syntax.

goToStore = (e) => {

}

Check the docs for more details on this

In case 1, as you said, the context is picked up from the environment. For case 2, you have to remember that the class syntax in ES6 is just syntactic sugar over the more cumbersome prototype syntax on which JavaScript relies upon to implement OOP.

Basically, in the second example, all you are doing is something like this:

function demo() {
   // this is the constructor
}
demo.prototype.goToStore = function() {
  // handler
};
demo.prototype.render = function() {
  return React.createElement('button', onClick: this.goToStore);
}

As you can see, the onClick property is just receiving a reference to the function. Whenever that function is called, no this will be binded, and it will be run in the context of the window object.

In older libraries, prior to the existence of modern transpilers, we used to do something like this ALL OVER THE PLACE:

function demo() {
   // this is the constructor     
   this.goToStore = this.goToStore.bind(this);
   // same for every other handler
}
demo.prototype.goToStore = function() {
   // handling code.
};
demo.prototype.render = function() {
  // this goToStore reference has the proper this binded.
  return React.createElement('button', onClick: this.goToStore);
}

Nowadays, this last example I put is automatically handled by all modern transpilers. Babel basically does the autobinding in the constructor when you use the fat arrow method syntax in any class:

class demo extends Anything {
  constructor() {
  }
  bindedMethod = () => {
    // my context will always be the instance!
  }
}

With subtle differences, all transpilers will move the definition of the bindedMethod to the constructor, where the this will be bound to the current instance running the constructor.

When the render code is executed, this refers to the component class, so it is able to reference the correct function. This means that element.onClick points to the goToStore method.

When that function is called in the browser, it's called from the context of the html element (i.e. element.onClick()). So unless the goToStore method is bound to the context of the component class, it will inherit this from the context of the element.

This answer provides some more relevant information and further reading.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!