React.js and debouncing onChange event is not working - javascript

I have implemented react component on change event like this:
NewItem = React.createClass({
componentWillMount: function() {
this._searchBoxHandler = debounce(this._searchBoxHandler, 500);
},
_searchBoxHandler: function(event) {
this.context.flux.getActions('order').setOrders(...);
},
render: function () {
...
var _self = this;
return (<TextField onChange={_self._searchBoxHandler} />)
})
});
I've done this implemention by checking this answer (Good idea section): https://stackoverflow.com/a/28046731/842622
But it's not working. I'm always having 'Cannot read property 'value' of null' from event object.
Am I missing something here?

You need to bind your context or use a closure to maintain scoping:
NewItem = React.createClass({
componentWillMount: function() {
this._searchBoxHandler = debounce(this._searchBoxHandler.bind(this), 500);
},
_searchBoxHandler: function(event) {
this.context.flux.getActions('order').setOrders(...);
},
render: function () {
...
var _self = this;
return (<TextField onChange={_self._searchBoxHandler} />)
})
});
A common mistake for new JavaScript programmers is to extract a method from an object, then to later call that function and expect it to use the original object as its this (e.g. by using that method in callback-based code). Without special care however, the original object is usually lost. Creating a bound function from the function, using the original object, neatly solves this problem.. - MDN
Some nice docs on binding and context:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
or you could use a 'fat-arrow' function to access parent class scope:
debounce((event) => { this._searchBoxHandler(event); }, 500);
Note: I wouldn't overwrite the declared function property in the componentWillMount invocation, instead you could store the debounced instance in another property like _debouncedSearchBoxHandler

Related

How to escape "this" problem in a javascript class?

get_O3(e)
{
e.preventDefault();
let station = document.getElementById(e.target.id);
let lon = station.getAttribute('lon');
let lat = station.getAttribute('lat');
let code_station = station.getAttribute('code');
this.get_previsions(lon, lat, "O3").bind(this).then((data) =>
{
console.log(data);
});
}
I have a "this" problem, when i call the function get_previsions i get the error :
Uncaught TypeError: this.get_previsions is not a function.
It might be because of the (e) parameter because when i do a console.log(this) it returns the target. I would like that this == my class.
Thanks for the help
At any given point you can check what the current this reference is pointing to by doing the 4 following rules:
New: Was the function called using new then the this points to the new instance.
Explicit Binding: Was the function called using Function#call, Function#apply or Function#bind
Implicit Binding: Was the function called by its owner? (i.e. owner.foo() or owner["bar"]())
Default Rule: If none of the other rules happen then this is set to the window object if the script is running in "use strict" mode otherwise undefined.
Event-listeners call a function using Explicit binding (callBack.call(target, ...)) so the this reference gets set to the target. To change the this reference you either need to wrap it and call it implicitly or use Function#bind.
Implicit call Example (+ closure):
var something = {
foo: function() {
var self = this;
addEventListener("click", function(e) {
self.bar(e);
});
},
bar: function() {
}
};
Explicit call Example (Function#bind):
var something = {
foo: function() {
addEventListener("click", this.bar.bind(this));
},
bar: function() {
}
};
I'm assuming you have a class defined similar to
class thing {
get_O3(e) { ... },
get_previsions() { ... }
}
There are a few options for you. First option, you can bind all functions to this in the constructor:
class thing {
constructor () {
this.get_03 = this.get03.bind(this);
this.get_previsions = this.get_previsions.bind(this);
}
get_O3(e) { ... },
get_previsions() { ... }
}
This can get awkward, especially if you have many functions. You can write a helper bindAll function, but a less awkward/verbose solution is to use a factory method instead, bypassing this altogether:
function makeThing {
const thing = {
get_O3(e) {
...
thing.get_previsions();
},
get_previsions() { ... }
};
return thing;
}
Eric Elliot on Medium has some good reading on the topic if you want to get more in depth.

Using Prototype with "Namespace" for existing object

I am looking to achieve something along the following.
HTMLSpanElement.prototype.testNS = {
_this: this,
testFunc: function() {
console.log(this.innerHTML) //undefined as expected as this is the testFunc object
},
testFunc2: function() {
console.log(this._this) //Window object
}
}
My goal is to add some helper functions directly to a span element in this case.
So, if I had the following:
<span>test</span>
I could find the span and call this code to return "test"
spanElement.testNS.testFunc()
I know that a function retains scope of it's parent when I do it like so...
HTMLSpanElement.prototype.testFunc = function() {
console.log(this.innerHTML)
}
But I am attempting to organize the code a bit and make it more obvious where the functions are coming from when I add them, and I can't seem to find a way to retain scope, when I do a normal JSON object grab the this scope into _this: this it just returns the global scope of "window".
Disclaimer: You shouldn't be trying to modify the prototypes on built-in types, especially host objects. It's a bad idea.
The reason your approach isn't working for you is that the functions are being called with the testNS object as the this.
You can get this to work if you define testNS as a property with a getter function, using Object.defineProperty. The reason this works is that the get function runs in the context of the object on which the property is being accessed (which would be the span):
Object.defineProperty(HTMLSpanElement.prototype, 'testNS', {
get: function() {
var _this = this;
return {
testFunc: function() {
console.log(_this.innerHTML)
},
testFunc2: function() {
console.log(_this)
}
}
}
});
var span = document.getElementById('mySpan');
span.testNS.testFunc();
span.testNS.testFunc2();
<span id="mySpan">Wah-hoo!</span>
A more "vanilla" approach is to just have testNS be a plain function and call it like one. This works because testNS is called in the context of the object on which it is being called (again, the span):
HTMLSpanElement.prototype.testNS = function() {
var _this = this;
return {
testFunc: function() {
console.log(_this.innerHTML)
},
testFunc2: function() {
console.log(_this)
}
}
}
var span = document.getElementById('mySpan');
span.testNS().testFunc();
span.testNS().testFunc2();
<span id="mySpan">Wah-hoo!</span>
When you call a function as foo.bar() then this inside bar refers to foo. Hence if you call the function as spanElement.testNS.testFunc(), this refers to spanElement.testNS.
_this: this, cannot work because this cannot refer to a <span> element.
To get access to spanElement from testFunc you could implement testNS as a getter:
Object.defineProperty(HTMLSpanElement.prototype, 'testNS', {
get: function() {
var element = this;
return {
testFunc: function() {
console.log(element.innerHTML);
},
};
},
});
document.querySelector('span').testNS.testFunc();
<span>foo</span>
Because it's a strange requirement I wrote a an equivalent strange solution :-)
Basically the createElement has been overriden in order to add a namespace object literal and then define a new function testFunc on top of the namespace using the instance of the element binded to the function
!function(){
var defaultNamespace = "testNS";
var createElement = document.createElement;
document.createElement = function(tag, namespace) {
var element = createElement.apply(document, arguments);
element[namespace || defaultNamespace] = {
testFunc : function() {
console.log(this.innerHTML);
}.bind(element)
};
return element;
}
}();
var span = document.createElement("span");

Access view property when calling render as a callback

I have to use the guid variable in the render() function, but I can pass it only to the constructor. I this code:
app.views.CompanyView = Backbone.View.extend({
el: '#company-view',
guid: '',
initialize: function (options) {
this.guid = options.guid;
},
render: function () {
var guid = this.guid;
}
});
I create my view like this:
app.currentView = new app.views.CompanyView({guid: guid});
Then I pass the render() function as a parameter to use it as a callback:
function call(callback){
callback();
}
call(app.currentView.render);
I tried this.guid, options and this.options too, but all of them were undefined. Is there a way to pass this variable to the render() function without using it's arguments or global variables? Here is a JsFiddle example.
When you call render through this:
function call(callback){
callback();
}
You're calling it as a plain function so this inside render will be window. Remember that this in JavaScript depends on how the function is called, not how it is defined (unless of course you're playing with bound functions).
You have some options:
Bind render to the view using _.bindAll, _.bind, $.proxy, Function.bind, ...
initialize: function() {
_.bindAll(this, 'render');
}
Demo: http://jsfiddle.net/ambiguous/GsUfY/
The more common approach these days is to pass a context with the function and then whoever calls the callback uses the appropriate context using call or apply:
function call(callback, context){
callback.apply(context);
}
Demo: http://jsfiddle.net/ambiguous/LnwPr/
Do it yourself by hand:
call(function() { v.render() });
This one usually takes the form of var _this = this; followed by an anonymous function that uses _this.some_method() instead of just passing this.some_method as a callback.
Demo: http://jsfiddle.net/ambiguous/K2Xj4/
I prefer the second option.
I see. When your render() is called by the callback function, the caller of the method is no longer the view itself, so the "this" inside your render will be the caller of the call function().
see this fiddle:
http://jsfiddle.net/cn8nN/2/
var CompanyView = Backbone.View.extend({
initialize: function (options) {
this.guid = options.guid;
},
render: function () {
console.log('hello');
console.log(this);
}
});
var v = new CompanyView({guid: 'the guid'});
function call(callbcak) {
callbcak();
}
call(v.render);
if you open the console, you will see "this " is actually the window.
to work around this, you want to bind the context to the view it self.
to do that, use _.bindAll();
initialize: function (options) {
_.bindAll(this, "render");
this.guid = options.guid;
}
jsfiddle: http://jsfiddle.net/cn8nN/3/

Understanding Backbone.js concepts

I am trying to learn Backbone.js.
In my app which uses Backbone with RequireJS, I have the following code;
define([
'base/BaseView',
'model/BaseModel',
], function(BaseView,
BaseModel){
var myView = BaseView.extend({
initialize: function() {
this.Summary = new resultSummary({
scenId : this.options.scenario.get("scenId")
});
},
renderCount : function(){
var self = this;
var currentStatus = self.model.get("myStatus");
}
render: function () {
var self = this;
var gridItems = [];
gridItems.push({
id: "company.status",
text: "Status",
width: "200px",
renderer: function() {
var partnerStatus = this.company.get("status");
}
});
}
}
});
I am not very clear with a few concepts;
What exactly would "this" represent when we say var self = this (I would like to understand this as a general question as well meaning when we use "this" anywhere in JS code)
Does "this" change if we are inside initialize Vs when we are in renderCount Vs when we are in "render" in the above code?
For the code "this.company.get("status")", what exactly does this.company represent? Is that referring to model ?
I think you are asking about closure?
we assign
var self = this;
so we can retain the scope of the class inside a nested function. on this case:
renderer: function() {
var partnerStatus = this.company.get("status");
}
Here's a great read: "Closures - JavaScript | MDN"
I probably won't be able to answer all the questions, since code in question is probably copied from larger code base.
Why do we use var self = this; and what exactly would this represent when the above code is executed ?
var self = this; is used to avoid scoping problems. Sometimes, when you use callbacks, this might change to some other object. Code mentioned in question doesn't benefit from it in any way this could be used directly.
Example when it is usefull - lets say, we need to listen to changes in model, and we want to attach handler in initialize method and call some logic from view on changes:
// view code
initialize: function() {
console.log(this); // 'this' points to view
this.listenTo(this.model, "change", function() {
console.log(this); // 'this' points to model
// calling 'this.someLogic();' would throw exception
});
},
someLogic: function() {
// ..
}
To avoid problem described in first example, you need to store 'this' from view context in some other variable (don't have to be named self).
Rewritten example:
// view code
initialize: function() {
console.log(this); // 'this' points to view
var self = this; // store this into variable that will won't be changed in different scope
this.listenTo(this.model, "change", function() {
console.log(this); // 'this' points to model
console.log(self); // 'self' points to view
self.someLogic(); // won't throw
});
},
someLogic: function() {
// ..
}
I recommend you to check how closures in JavaScript work. It is usefull not only for Backbone, but for JavaScript development in general.
Does "this" change if we are inside initialize Vs when we are in renderCount Vs when we are in "render" in the above code?
No, Backbone will point 'this' to view object, which contains those methodd.
For the code "this.company.get("status")", what exactly does this.company represent? Is that referring to model ?
No idea really, I can only guess, that it is some property from BaseView

Assigning scope amongst jQuery.getJSON and a JS.Class

I'm trying to assign some JSON data to a property of a JS.Class instance.
var MyClass = new JS.Class({
initialize: function(uuid) {
this.uuid = uuid;
},
write: function() {
$.getJSON(url+"?callback=?", {}, function(data) {
Assign(data);
});
function Assign(data) { this.content = data; };
}
});
var m = new MyClass("uuid_goes_here");
m.write();
The JSON is received asynchronously, which is why there's a function call within the $.getJSON callback.
The problem I have now is that the this.content within the Assign function is not within the scope of the instance method named write. So whereas this.uuid returns correctly, this.content remains undefined (as you would expect).
Any ideas on how to correct this? I've tried using a global variable as a workaround but the async call doesn't allow for that (plus it's a crappy solution).
Some points to note, in case they matter: I have to use JSONP, so the "?callback=?" has to stay, and I'd like to keep it async.
I would usually go for either czarchaic's version, or replace Accept with a bound method from the object. What you have to bear in mind is that calling Accept() like that (as a function call rather than a method call) will bind this to the global object, i.e. window. I'd try this:
var MyClass = new JS.Class({
initialize: function(uuid) {
this.uuid = uuid;
},
write: function() {
$.getJSON(url+"?callback=?", {}, this.method('setContent'));
},
setContent: function(data) {
this.content = data;
}
});
See http://jsclass.jcoglan.com/binding.html for more info.
You should cache the current instance in the write method and update it after ajax.
write: function() {
var self=this;
$.getJSON(url+"?callback=?", {}, function(data) {
self.data=data;
});
}

Categories

Resources