Something that is puzzling me is why when I define a react component class, values contained in the this
object are undefined in methods defined (this
Functions in ES6 Classes - the case is explained very well by @Felix Kling. Every time you call a function on an object, this
points to the object.
Lifecycle methods in React.Component - whenever React instantiates your component like myComponent = new MyComponent()
it knows which object to call the lifecycle methods on, namely myComponent. So a simple call myComponent.componentDidUpdate()
makes this
available in the componentDidUpdate
lifecycle method. Same for the other lifecycle methods.
Handlers & Bound in React.Component - this.state
is undefined
, because this
is actually window
- log it and see. The reason is that React invokes handlers on the global context, unless you have the handler bound to another context which overrides window
(see @Phi Nguyen's answer also).
I think they have done that to allow you more flexibility, because in complex applications your handler may come from another component passed through props and then you would like to have the possibility to say: "Hey, React - this
is not my component, but it's parent."
React's documentation is a bid misleading when it says
Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind this to the instance.
What they mean is that
var dog = new Dog('Mitzie');
speak = d.speak;
dog.speak() // this will be dog, because the function is called on dog
speak() // this will be window, and not dog, because the function is not bound
Just always put the autoBind(this);
code in your constructor and never worry about method pointers.
npm install --save auto-bind-inheritance
const autoBind = require('auto-bind-inheritance');
class Animal {
constructor(name) {
autoBind(this);
this.name = name;
}
printName() { console.log(this.name); }
...
}
let m = new Animal('dog');
let mpntr = m.printName;
m.printName() //> 'dog'
mpntr() //> 'dog', because auto-bind, binds 'this' to the method.
Event handlers in the component will not be bound automatically to the component instance like other methods ( life cycle methods...).
class MyComponent extends React.Component {
render(){
return (
<div onClick={this.renderElements}>
{this.renderElements()} <-- `this` is still in side the MyComponent context
</div>
)
}
}
//under the hood
var instance = new MyComponent();
var element = instance.render();
//click on div
element.onClick() <-- `this` inside renderElements refers to the window object now
Check this example to understand this
more :
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
run(){
console.log(this.name + ' runs');
}
speak() {
console.log(this.name + ' barks.');
this.run(); <-- `this` is still in the Dog context
return {onRun : this.run};
}
}
var d = new Dog('Mitzie');
var myDog = d.speak();
myDog.onRun() <-- `this` is now in the global context which is the `window` object
You can check this article for more information.
1. Arrow Functions:
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (
does not bind its own this, arguments, super, or new.target
). Arrow functions are alwaysanonymous
. These function expressions are best suited for non-method functions and they can not be used as constructors.
Function.prototype.bind():
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
2.Component Specs and Lifecycle
To be absolutely clear: Most lifecycle methods are not bound, but called on an instance using the dot notation (true for
componentWillMount
,componentWillUnmount
,componentWillReceiveProps
and so on...), exceptcomponentDidMount
which is bound to the instance since it gets enqueued into the transaction.
The value of this
primarily depends on how the function is called. Given d.speak();
, this
will refer to d
because the function is called as an "object method".
But in <div>{this.renderElements}</div>
you are not calling the function. You are passing the function to React which will call it somehow. When it is called, React doesn't know which object the function "belonged" to so it cannot set the right value for this
. Binding solves that
I actually think what you really want is
<div>{this.renderElements()}</div>
// call function ^^
i.e call the function as an object method. Then you don't have to bind it.
Have a look at MDN to learn more about this
.