JavaScript React setInterval "this" issue - javascript

I'm trying to learn React and I don't understand why this does not do anything when I try without an arrow function
componentDidMount() {
var self = this
this.timer = window.setInterval(function () {
self.increment
}, 1000)
}
Is the correct pattern self.increment()?
So why do I need the () when I don't need it in:
componentDidMount() {
this.timer = window.setInterval(this.increment.bind(this),1000)
}

You are not invoking self.increment, you are properly scoping this for your code to work. Maybe it appears it's not working because your interval's callback does not do anything :)
Also, here's a simpler version of your code, you could omit the .bind if your function does not need to access your component's scope, but typically in react you'd want access to that:
this.timer = setInterval(this.increment.bind(this), 1000)

Related

Getting a timer ID within its callback in setTimeout()?

I am using TypeScript and Angular to build an app. In a component, I create a few timeouts and I want to be sure to cancel them when my component is destroyed. To do so, I have a Set<number> that keeps track of active timeouts. When my component is destroyed, I simply iterate over this set and cancel all my timeouts. When I create a timeout, I add its ID to the Set. My problem is that I currently don't have a way to remove the ID from the Set when the timeout has executed. I want to be able to get the timeout ID inside my callback function to remove it from the Set. Is there a way to do this?
Here is some code that could explain:
export class CustomComponent implements OnDestroy {
private activeTimeouts: Set<number> = new Set<number>();
// Method called when component is destroyed
ngOnDestroy() {
this.activeTimeouts.forEach((item) => {
clearTimeout(item);
});
}
someFunction() {
const timerID = setTimeout(() => {
console.log("Hello World");
// How can I get timerID from here?
}, 1000);
this.activeTimeouts.add(timerID);
}
}
As you can see from my code, I would like to be able to remove the timerID after console.log("Hello World"); is executed. Is there any way to do this?
You can indeed use the timerID in the scope of setTimeout.
The function that's there within setTimeout is a closure.
Closure helps when functions are probably called later on in code but need to refer to variables outside their local scope when they're called.
const timerID = setTimeout(() => {
useTimerIdentifier(timerID);
console.log("timer accessed");
}, 3000);

Using this vs using variable in JS

I am having tough time figuring out when should we preferably use this keyword and when shouldn't we use it..
For example, I was earlier doing something like this.
let socket;
class something extends Component {
componentDidMount() {
socket = openSocket('https://coincap.io');
}
componentDidUpdate() {
socket.on('trades', (tradeMsg) => {
}
componentWillUnmount() {
this.socket.disconnect();
}
then someone re-structured my code did something like
class something extends Component {
componentDidMount() {
this.socket = openSocket('https://coincap.io');
}
componentDidUpdate() {
this.socket.on('trades', (tradeMsg) => {
}
componentWillUnmount() {
this.socket.disconnect();
}
[Question:] While, Both the code works and do the job, I am perplexed between understanding which methods should we use and why did he used this keyword?
You are dealing with ES6, which takes out a lot of ambiguity pertaining to the this keyword in the language.
Quite simply, this means (in the more traditional sense) the current scope; however, in JavaScript, this means the object calling the function as opposed to the current scope.
As mentioned previously, ES6 takes out a lot of complexity regarding variable hoisting and the likes.
Coming to your question, the reason it was refactored to this.something was because when your class is called (now, remember, JavaScript follows prototypal inheritance and is a first-class language; meaning that function prototypes are the class bases and functions can be passed as parameters) it will act like a function and the this keyword will refer to the object or the context where it was called.
If we have the following bit of code:
let foo = 'bar';
const fn = () => { console.log( this.foo ); }
fn();
The execution context would be window and the foo variable would be window.foo; hence, this.foo would translate to window.foo.
Similarly, your code will be transpiled something like:
var socket = new Socket();
function className() {}
className.prototype.someFunction = function() {
console.log( socket );
}
Here, the this keyword would make sure that the current context is used instead of local variables which you might use within your function.
I hope this makes it clear. Here is a great article to understand the this keyword! :)
The problem isn't the difference between this and a variable per se. It's that this refers to class instance and allows to have multiple class instances and thus multiple socket instances, while socket variable refers to specific socket instance and will be overridden in subsequent component instances.
The component will malfunction if there's more than one component instance at time, because socket variable refers to latest openSocket('https://coincap.io') result.
Also, this
let socket;
class something extends Component {
componentDidMount() {
socket = openSocket('https://coincap.io');
}
componentDidUpdate() {
socket.on('trades', (tradeMsg) => {
}
componentWillUnmount() {
this.socket.disconnect();
}
will result in error on component unmount because there's no this.socket.

setInterval function without arrow function

I am learning about react components following the documentation https://facebook.github.io/react/docs/state-and-lifecycle.html
Why do we need to use arrow function here:
this.timerID = setInterval(() => this.tick(), 1000);
Why can't I just say (obviously it doesn't work)
this.timerID = setInterval(this.tick(), 1000);
The first argument for setInterval is of type function. If you write this:
this.timerID = setInterval(this.tick(), 1000);
…then you don't pass a function, instead you execute the function this.tick immediately and then pass the value returned by that function call as an argument.
You could write it like this:
this.timerID = setInterval(this.tick, 1000);
If you omit the parentheses, you pass a reference to your this.tick function, which is then executed by setInterval after 1000 milliseconds.
setInterval takes function as first argument, in the second case it is getting a returned value
Change it to
this.timerID = setInterval(this.tick.bind(this), 1000);
or
this.timerID = setInterval(() => this.tick(), 1000);
which is probably what you need when you want to bind the function to the React context.
See this answer on why you need to bind functions in React
If you don't you could have simply written
this.timerID = setInterval(this.tick, 1000);
Why do we need to use arrow function here
Answer is simple :
see the result inside live script example...
class Persons{
scopeOne(){
// your will see the result this will display our current object 'Persons'
setInterval(function(){
console.info(' SCOPE ONEo : '+this);
},3000);
}
scopeTwo(){
setInterval(function(){
console.info(' SCOPE TWO : '+this);
}.bind(this) ,3000);
}
scopeThree(){
setInterval(() => { console.info(' SCOPE THREE : '+this) },3000);
}
}
let p = new Persons();
p.scopeOne();
p.scopeTwo();
p.scopeThree();
in first scope the result is WINDOW OBJECT so we cannot access our current class scope
so in 2nd scope we using scope with bind(this) that helps to bind our current object scope,
and in third which do same as 2nd scope calling the current object....
The simple answer to that is that the tick function needs to be able access the context of its use/ this, which is the Clock component. In other words it needs to be bound to the Clock component so that it works in the context of the Clock component and not the global context which is everything outside of the Clock component.
Functions within React classes are not bound by default and failure to bind the function will return undefined instead of the expected result.
There are several ways to bind the tick function to the Clock component example from the Reactjs website.
Arrow functions can access this which is why they are used in the example. In other words, writing an arrow function implicitly means it binds its contents to the local context (the Clock component in this case). Read more on that in this Medium article
Bind the tick function in the constructor
class Clock extends React.Component{
constructor(props){
super(props);
this.state={date: new Date()}
this.tick=this.tick.bind(this)
}
Bind the tick function in the setInterval method
componentDidMount() {
this.timerID = setInterval(this.tick.bind(this),
1000
);
}
Transform a function called tick into a variable with an anonymous function, so you can pass it as an argument without parentheses.
Taking this code (https://codepen.io/gaearon/pen/amqdNr?editors=0010) as a base, you can update like this:
componentDidMount() {
this.timerID = setInterval(this.tick,1000);
}
tick = () => {
this.setState({
date: new Date()
});
}
Generally speaking, whenever you are going to pass a function as a parameter in js that uses an object variable, you need to BIND it with its object, so you don´t lose this reference. The two ways to do this are: using arrow functions or using bind().
You need to supply a function reference, you are trying to invoke a function, unless that function returns a function reference, your code will not work
It should look like so
this.tick = function() { .... }
this.timerID = setInterval(this.tick, 1000);
If you are not using arrow function then your code should look something like this:
this.timerID = setInterval(function(){ this.tick() }, 1000);
Technically, you should be able to use the second code snippet if this.tick() returns a function.

Function timeout

I'm trying to write a code that adds a class to a div for a limited time, and then removes it.
I tried using javascript's setTimeout, and jQuery's delay, but nothing works.
The element is SET but never REMOVED.
Here's the come I came up with:
window.onload = function() {
$(".button").click(handler);
}
function handler() {
$(this).addClass("onclick");
setTimeout(function() { $(this).removeClass("onclick"); }, 3000); // JS's setTimeout
$(this).addClass("onclick").delay(3000).removeClass("onclick"); // jQuery's delay
}
I don't get what's wrong... I even tried writing a second handler for the setTimeout function.
Thanks in advanced.
The problem you're having is that this is different within the function you're passing to setTimeout than it is outside it. The usual fix is to use the closure by creating a variable to hold it, and using the variable instead:
function handler() {
var $elm = $(this);
$elm.addClass("onclick");
setTimeout(function() {
$elm.removeClass("onclick");
}, 3000);
}
There I've also use the var to cache the result of $(this) because there's no point in doing it more than once.
More background:
In JavaScript, unlike some languages that look similar, this is defined entirely by how a function is called. When you use setTimeout, the way the function gets called will make this be the global object (window, on browsers), so that's why $(this).removeClass(...) wasn't working.
More on this if you're interested:
Mythical methods
You must remember this
this inside the setTimeout call does not refer to the clicked element.
Change it to this:
function handler() {
var t = $(this);
t.addClass("onclick");
setTimeout(function() { t.removeClass("onclick"); }, 3000);
}
Working example - http://jsfiddle.net/5vakN/
Reference for how this works in javascript - http://bonsaiden.github.com/JavaScript-Garden/#function.this

window.setInterval from inside an object

I'm currently having an issue where I have a javascript object that is trying to use setInterval to call a private function inside of itself. However, it can't find the object when I try to call it. I have a feeling that it's because window.setInterval is trying to call into the object from outside but doesn't have a reference to the object. FWIW - I can't get it to work with the function being public either.
The basic requirement is that I may need to have multiple instances of this object to track multiple uploads that are occurring at once. If you have a better design than the current one or can get the current one working then I'm all ears.
The following code is meant to continuously ping a web service to get the status of my file upload:
var FileUploader = function(uploadKey) {
var intervalId;
var UpdateProgress = function() {
$.get('someWebService', {},
function(json) {
alert('success');
});
};
return {
BeginTrackProgress: function() {
intervalId = window.setInterval('UpdateProgress()', 1500);
},
EndTrackProgress: function() {
clearInterval(intervalId);
}
};
};
This is how it is being called:
var fileUploader = new FileUploader('myFileKey');
fileUploader.BeginTrackProgress();
Use this
intervalId = window.setInterval(UpdateProgress, 1500);
setInterval with a literal argument will eval this in the global scope where UpdateProgress is not accessible.
Because it is an eval expression, it does not have access to the scope that setInterval is created in. Try:
intervalId = window.setInterval(UpdateProgress, 1500)
It is generally good practice to avoid eval style expressions wherever possible. For instance, if you wanted to call several functions from the same timer, you would use an anonymous function instead of a string.
window.setInterval(function () {
function1();
function2();
}, 1500)
See also
Why is using javascript eval() a bad idea?
Anonymous function - Wikipedia
+1 to Andy E's head (I can't upvote yet, doh!)
Another gotcha that could get you is if you use this from within the called function.
Then doing exactly what Andy has with this addition should get you by.
var that = this;
window.setInterval(function() {
function1.apply(that);
function2.apply(that);
}, 1500);

Categories