setInterval and @tracked

I don’t understand why whatTimeIsIt is not updating in the DOM. Ideas?

The timer is ticking, I can set a breakpoint in the console and I see that whatTimeIsIt is changing…

@tracked whatTimeIsIt

timerId = null

updateWhatTimeIsIt() {
    this.whatTimeIsIt = (new Date()).toISOString()
}

constructor() {
    super(...arguments)
    this.updateWhatTimeIsIt()
    this.timerId = setInterval(this.updateWhatTimeIsIt, 1000 )
}

The display doesn’t update

<div>
<div>Timer</div>
<div>{{this.whatTimeIsIt}}</div>
</div>

This is just a guess but feels like you may need to bind the callback to this so it has the right context. Something like:

    this.timerId = setInterval(this.updateWhatTimeIsIt.bind(this), 1000 )

2 Likes

Good guess.

What does .bind do?

So when you refer to this in javascript it can point at pretty much anything. If you’re calling a class method from a class method, for example, the this context is the same, but in the case of a callback like setInterval the browser is invoking the callback function and it will use the “global” context as this instead of the class which you intended. To solve this you need to manually bind the value of this to the class so it’s correctly bound when the callback is triggered.

Ember handles a lot of this stuff for us so we rarely have to call .bind (for example the @action decorator actually does the binding that we need) but when using browser APIs like setInterval sometimes it is necessary.

Here are a few links if you want to read more on the subject:

EDIT: as for the reason your original code didn’t work but also didn’t blow up setting a var on the global context is valid, but that var wasn’t tracked and was totally different from what was being rendered in the DOM, so the DOM obviously didn’t update.

2 Likes

You can use the action decorator on the class method to achieve the same. That might be easier to reason about than using .bind explicitly.