I need to access a React component within a function nested within a a handler function.
When in a nested function this does not give a reference to the component
so I explicitly declare it instead of doing the usual this.setState(...)
Is there a cleaner way to do this than my following code?
React.createClass({
uploadImage : function() {
...
var componentReference = this
xhrRequest.onreadystatechange = function()
{if(request.readyState==4) {
componentReference.setState({uploadStatus: request.status});
}
}
This is just how JavaScript works:
You'll have to keep a reference to whatever you want to access in a function that's called in a different context (like you implemented it with your variable)…
or
…bind what this points to at the time the function is called. There's no need to use a library (like lodash) because ECMAScript 5.1 has his built-in, and you can use it in any recent-ish browser (Chrome 7+, Firefox 4+, IE 9+, Safari 5.1+), see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
If you need to support older browsers, you can implement a bind function yourself, see for example Underscore.js' Function.prototype.bind implementation: http://underscorejs.org/#bind
The most idiomatic way to do this is with the native .bind() method, no libraries required:
React.createClass({
uploadImage : function() {
// ...
xhrRequest.onreadystatechange = function() {
if(request.readyState == 4) {
this.setState({ uploadStatus: request.status });
}
}.bind(this)
// ...
}
})
.bind(this) sets the context of this inside the .onreadystatechange closure. It's a bit like passing the scope to the inner function.
If you are using e.g. lodash, you can do it like this:
xhrRequest.onreadystatechange = _.bind(function () {
if ( request.readyState == 4 ) {
this.setState({ uploadStatus: request.status });
}
}, this);
Or, if you use jQuery, you can proxy.
You can simply use the Function bind method in plain old JS like function() {}.bind(this) and then "this" inside your function will be your React component.
You can see the definition of bind() here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
You could put the onreadystatechange handler on the “class” itself, profiting from React’s auto binding.
You could also keep a reference to setState, but I’m not sure whether that’s auto-bound as well. Try it out.
Last but not least, since you are probably using a transpiler anyway, you could use an ES6 arrow function. Arrow functions use the “outer” value of this (lexical this)
Related
Im trying to build an app with Cytoscape.js and Angular2+.
The Core part take place in a single service (cy.service).
I add all Eventlisteners in a single function and call it after the cy init.
eg:
initEventlisteners(){
cy.on('tap', this.handler);
cy.on(...);
...
}
handler(event: any){
console.log('something');
}
If I wrap console.log in a helper-function like that:
helper(){
console.log('something');
)
And use it in the handler-function
handler(event:any){
this.helper();
}
It calls: helper() is not a function.
Thats a big problem that a cant use other functions inside the handler.
Any ideas how to solve that problem ?
That's because with binding like this cy.on('tap', this.handler); you are loosing scope of variable. You should use it in this way cy.on('tap', (event) => { this.handler(event) }); (if it's passing any event object) or cy.on('tap', this.handler.bind(this));.
The frist approach with () => { } is called arrow function, which preserve current scope of variables. It's a new feature of ES6 specification. However you don't have to be afraid, angular compiler will convert it to backward compatible to ES5.
The second approach .bind(this) just simply binding this method to current scope where it's was called.
I find that it's very common to have code like (this is TypeScript, but the equivalent JS is fairly obvious and this is really a JS problem, although one that TS could solve):
class Foo {
someField;
someMethod() {
doSomethingTakingACallback(function() {
this.someField; // Fails because `this` is not the instance of `Foo`
});
}
}
The solution, of course, is to use Function.bind() like so:
someMethod() {
doSomethingTakingACallback(function() {
this.someField; // Works as expected
}.bind(this));
}
Now the problem is that I have callbacks that must access the object instance a lot (ie, I need to be calling bind(this) on many callbacks). I've now wasted more time on bugs resulting from this than I'd like to admit. Is there some alternative? Am I missing some easier way to do this? Is there any reason that this is the default behavior as opposed to the function that we get from calling bind(this)?
One solution I know of is to do something like var me = this and then call me.someField (etc). It's a little nicer looking when I have many callbacks or nested callbacks. Although then I lose out on the iconicness of this, which I feel makes it the most clear where the member is located (on the class that I'm writing).
Typescript and ES6/ES2015 both support the "fat arrow function" syntax, which allows you to use this the way you do in most other languages - to refer to the class instance object.
e.g.
someMethod() {
doSomethingTakingACallback(() => {
this.someField;
});
}
This compiles to the following code:
Foo.prototype.someMethod = function () {
var _this = this;
doSomethingTakingACallback(function () {
_this.someField;
});
};
ES6 Arrow functions keep the this of the surrounding scope and thus do not need binding.
ES7 will (possibly) have the :: bind operator
Assign this to another variable outside of your callback method and use that.
class Foo {
someField : any;
someMethod() {
var that = this;
doSomethingTakingACallback(function () {
// use that.someField;
});
}
}
So I have most of my functions and variables organized into small object-based modules, like so:
module1: {
someVariable: false,
someFunction: function(e) {
do some stuff using someVariable
},
someFunction2: function(e) {
do some other stuff
}
}
And I call these functions as callbacks during various events, like so:
$(function() {
$('.thing').on('mouseenter', module1.someFunction);
}
Now, from within someFunction, I would expect the 'this' keyword to refer to the object in which the function is contained. Instead, it refers to the DOM element that triggered the event that fires the function. Is there anyway I can get access to, say the someVariable variable in the function's containing object other than writing module1.someVariable?
The shortest answer is to try this:
$(function() {
$('.thing').on('mouseenter', function(e) {
module1.someFunction(e);
});
}
The 'this' value is only set to the object the method is attached to if the method is invoked directly on the object:
module1.someFunction(); // direct invocation, 'this' will be set properly
var tempFunc = module1.someFunction;
tempFunc(); // the function was removed first. 'this' will NOT be set
In your case, you are pealing the method off of the object and handing it to an event handler. The event handler doesn't know about the object and doesn't perform a direct invocation.
In fact, the event handler explicitly overrides the context because that is how the jQuery API is defined. You have to explicitly override it back if you want the behavior you're talking about.
Using a library like underscore.js you could also bind the function as you pass it off to the event handler.
$(function() {
$('.thing').on('mouseenter', _.bind(module1.someFunction, module1));
}
I believe that Object.bind is supposed to be natively supported in the future with no libraries, but you can't yet rely on the older browsers to support it.
Is it possible to run the method on an existing object on timeout of window.setInterval method. I can emulate the same by having some global variable and calling the method of this global variable in setInterval, but i wanted to know if this is possible using the method directly.
Best Regards,
Keshav
Yes, you can do this. You need a helper function to make a new function that has your existing object "bound":
var someRandomObject = {
someMethod: function() {
// ... whatever
},
// ...
};
// this is a "toy" version of "bind"
function bind(object, method) {
return function() {
method.call(object);
};
}
var interval = setInterval(bind(someRandomObject, someRandomObject.someMethod), 1000);
Now when the interval timer calls your method ("someMethod"), the "this" pointer will reference the object.
That version of "bind" is simplified. Libraries like Prototype, Functional, jQuery, etc generally provide more robust versions. Additionally, the "bind" function will be a native part of Javascript someday — it already is in some browsers.
I have a makeshift events system in JS and the attacher looks like this:
events.attach(events.NEW_TASK,this.update);
Where this.update is a callback/handler/function. The problem is, when the dispatcher tries to call this handler, it loses it's original context. How can I bind the handler with the context of the object that calls the attach function without passing this as an argument and using that?
Edit: I should also mention that it will be somewhat of a framework so it has to be simple for other developers so preferably not needing to store the context in another variable.
Another option, using the Function.prototype.bind method, part of the new ECMAScript Standard:
events.attach(events.NEW_TASK, this.update.bind(this));
Until ECMAScript 5 has wide adoption in browsers, you could use your own implementation of Function.prototype.bind:
function bind(func, thisObj) {
return function() {
func.apply(thisObj, arguments);
};
}
events.attach(events.NEW_TASK, bind(this.update, this));
You could store the reference of "this" in a local variable and try calling the function using it
var that = this;
events.attach( events.NEW_TASK, function( ) { that.update(); } );