2

In my NextJS/React typescript app I'm using a setTimeout.

There is a bug in React apps causing setTimeout's to get called instantly, which I then found a fix in this answer here: ReactJS: setTimeout() not working?

Below is my code, but I'm getting the following typescript error on the this on this.resetNotification

any 'this' implicitly has type 'any' because it does not have a type annotation.ts(2683) Board.tsx(158, 7): An outer value of 'this' is shadowed by this container.

@bind
resetNotification(): any {
  console.log('resetNotification...');
  this.setState({ notificationClass: 'spin-in-notification' });
  this.props.setNotification('', false);
}

@bind
private handleNotificationClick() {
  this.setState({ notificationClass: 'slide-out-bck-top' });

  setTimeout(
    function() {
      this.resetNotification();
    }
    .bind(this),
    500
  );
}

enter image description here

Leon Gaban
  • 36,509
  • 115
  • 332
  • 529

3 Answers3

10

Do it with arrow function on setTimeout for heredate parents props

setTimeout(
  () => {
  this.resetNotification();
  }......
Jordi Castillo
  • 683
  • 1
  • 5
  • 13
4

If you still want to use function () {} syntax, you can pass this as the first parameter to the function, along with a type annotation. Like this:

  setTimeout(
    function(this: Board) {
      this.resetNotification();
    }
    .bind(this),
    500
  );

I'm assuming since the file is called Board.tsx that your component is <Board>. If not, change the type annotation for this.

sleighty
  • 895
  • 9
  • 29
  • Thanks, great answer too.. +1 but I'm trying to avoid obvious Types, feel that would be too verbose in this situation. – Leon Gaban Mar 13 '19 at 22:45
  • 1
    Yeah for sure just thought I'd add that in case you _really_ wanted to use `function` syntax – sleighty Mar 13 '19 at 22:47
0

Just pass the function as a reference, no need to wrap it in an anonymous function or even bind it, which creates yet another function.

setTimeout(this.resetNotification, 500);

There is no bug in React calling setTimeout instantly, so if you were puzzled by it, consider this.

function doSomething() {/* */}

const a = doSomething() // immediately invokes and assigns a result
const b = doSomething   // stores a reference

// call later
const x = a() // error
const y = b() // invokes doSomething and assigns a result

And in your case, the "bug" you mention is basically the same thing.
When you register your setTimeout callback, you mistakenly immediately call it, where instead you should pass a reference to it.

function doSomething() {/* */}

// wrong
setTimeout(doSomething(), 500) // This is basically the same as writing the `a` from above
setTimeout(a, 500)             // like this. See the problem? a() cannot be called later.

To fix it, you have three options.

  1. pass a reference
setTimeout(this.resetNotification, 500)
  1. wrap in an anonymous arrow function which is transparent to this,
    meaning it captures the outer (parent) this.
    note that this wraps your function in another function every time you call this
setTimeout(() => this.resetNotification(), 500)
  1. wrap in a standard anonymous function, but since it comes with it's own this, you must bind it to the this of the parent.
    note that this wraps your function in another function AND THEN binds it, which creates a third function every time
setTimeout(function(){this.resetNotification()}.bind(this), 500)

Enter setTimeout

setTimeout(callback, timeout?, param1?, param2?, ...)

It seems people don't realise that setTimeout and setInterval actually accept optional unlimited parameters. The reason is to make calling the callback simpler, so instead of this

setTimeout(
  function(){
    this.doSomething(true, "string", someVariable)
  }.bind(this),
  500
)

You can write this

setTimeout(this.doSomething, 500, true, "string", someVariable)

Isn't that beautiful and elegant?


Also you should fix your resetNotification() function's signature to resetNotification(): void not :any as it doesn't return anything.

Qwerty
  • 29,062
  • 22
  • 108
  • 136