I know about the general theory of this
binding (function call site what matters, implicit, explicit binding, etc...) and the methods to solving the problem of this binding in React so it always points to what I want this
to be (binding in constructor, arrow functions, etc), but I am struggling to get the inner mechanisms.
Take a look at these two pieces of code:
class demo extends React.component {
goToStore(event) {
console.log(this)
}
render() {
<button onClick={(e) => this.goToStore(e)}>test</button>
}
}
vs.
class demo extends React.component {
goToStore(event) {
console.log(this)
}
render() {
<button onClick={this.goToStore}>test</button>
}
}
What I know is that:
- in both versions we end up successfully in the goToStore method, because the
this
inside therender()
method is automatically bound (by React) to the component instance - the first one succeeds because of that,
- the second one fails, because class methods in es6 are not bound to the component instance, thus resolving
this
in the method toundefined
In theory in the first version as far as I understand, the following occurs:
- the button click handler is an anonymous arrow function, so whenever I reference
this
inside of it, it picks upthis
from the environment (in this case fromrender()
) - then it calls the
goToStore
method, that is a regular function. - because the call seems to fit the rules of implicit binding (
object.function()
)object
will be the context object and in such function calls it will be used asthis
- so, inside the
goToStore
method the lexically picked up this (used as a context object) will resolve to the component instance correctly
I feel 3. and 4. is not the case here, because then it would apply to the 2. case:
<button onClick={this.goToStore}>test</button>
Also with this
the context object.
What is happening exactly in this particular case, step-by-step?
As the MDN docs states
So you would think of
as being an anonymous function which can be written as
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:
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:
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, causingthis
to be undefined inside the function as the function runs on the context of thewindow
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.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:
As you can see, the
onClick
property is just receiving a reference to the function. Whenever that function is called, nothis
will be binded, and it will be run in the context of thewindow
object.In older libraries, prior to the existence of modern transpilers, we used to do something like this ALL OVER THE PLACE:
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:
With subtle differences, all transpilers will move the definition of the
bindedMethod
to the constructor, where thethis
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 thatelement.onClick
points to thegoToStore
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 thegoToStore
method is bound to the context of the component class, it will inheritthis
from the context of the element.This answer provides some more relevant information and further reading.