I think the title is pretty self-descriptive.
I\'ve building react components using the class notation, and I noticed that while handleSomething
has to be m
The 'this' keyword in a function is determined by the executing scope of the function. For example, the this
in someFunction
when calling with obj.someFunction()
will be obj
.
A more concrete example:
function handleClick() {
console.log(this.state.value);
}
var state = { value: 1 }; // declare a var in window
console.log("handleClick()");
handleClick(); // Logged 1. The 'this' in the method will be window, because the method is called in window
var obj = {
state: { value: 2 },
handleClick: function() {
console.log(this.state.value);
},
};
console.log("obj.handleClick();");
obj.handleClick(); // Logged 2. The 'this' is referred to obj because the method is called in obj.
// let's reassign the function to a temp var in window
var temp = obj.handleClick;
console.log("temp()");
temp(); // Logged 1. The 'this' in the function is referred to window because the method is called in window.
console.log("window.temp()");
window.temp(); // this is equal to the one above.
console.log("temp.bind(obj)");
temp.bind(obj)(); // Logged 2. Bind the method and call the method, so the 'this' in the function is referred to obj.
console.log("temp.bind(this)");
temp.bind(this)(); // Logged 1. Since this in the executing scope is window. This effectively is the same calling in this.
console.log("temp.bind(window)");
temp.bind(window)(); // Logged 1. This is equal to the one above.
Try it here: https://codepen.io/anon/pen/OvOpEa?editors=0012
A blog post about this: https://hackernoon.com/understanding-javascript-the-this-keyword-4de325d77f68
If you look at render
, componentWillMount
, and handleSomething
you defined in your class, it will become apparent why you need to bind your handler to this
.
// Rerender
ReactCurrentOwner.current = workInProgress;
var nextChildren = void 0;
{
ReactDebugCurrentFiber.setCurrentPhase('render');
nextChildren = instance.render();
if (debugRenderPhaseSideEffects) {
instance.render();
}
ReactDebugCurrentFiber.setCurrentPhase(null);
}
This is how react call redner()
, where the instance is the object instance that has state, props, etc. You can try it very easily by putting a breakpoint in your render method and go back a call stack.
For example, if you define your class like this, with handleSomething
as the onClick
callback method of the button.
class Button extends Component {
handleSomething() {
// 'this' will be undefined.
}
render() {
return ();
}
}
If you click the button, this is how react calls the onClick handler method.
function callCallback() {
fakeNode.removeEventListener(evtType, callCallback, false);
// This is where react calls your method.
func.apply(context, funcArgs);
didError = false;
}
where func
is handleSomething
, and context
is usually undefined
in my debugging experience, and funcArgs
is the arguments that being passed in the function.
apply
is similar to bind
. The first argument is used to specify the this
of the function, and the second argument is an array of parameters to pass into the function.
See MDN for more information about apply
: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
In this case, the method handleSomething
is being called with undefined
as this
; Therefore, if you didn't bind the method, your this
will be undefined
.
I noticed that while handleSomething has to be manually bound to this, render and componentWillMount do not. Are method bound to this already?
They are called with the instance of your class, so they already have this
as your instance without using bind
. I guess you can kind of say it's already bound to this
.
Is it ok to bind manually for notationally consistency's sake?
You don't need to bind this
with react's lifecycle methods. If you really want to, I guess you can bind those methods to this
as well (there might be some side effects that I don't know, since I didn't really look that deeply into their source), but this is like doing obj.handleClick.bind(obj)();
instead of obj.handleClick();
. It is unnecessary and will spend some clock cycles doing something that's not needed.