Fifth Dimension Dragon
Fifth Dimension Dragon

Reputation: 197

setInterval in a promise, only executes once in React

I'm trying to create a countdown timer with one second ticking away each second. The interval works only one time and does not execute. The problem is that I do not have this.state.timetill until a server call is made, then I can begin the timer. Therefore, the setInterval function has to be tucked away in a promise, where it executes only one. The timetill variable however can be a constant, once retrieved it does not change.

componentDidMount = async () => {
const that = this;
*do stuff*
this.someFunction().finally(() => {
/*This right here, below is not working */
setInterval(that.createTimer(), 1000);  
});
}

My JSX is such

{this.state.setTimer}

function in question, the time has to be divided by 1000 since javascript uses miliseconds

createTimer = async timerNow => {
var timerNow = (await new Date().getTime()) / 1000;
// Find the distance between now and the count down date
var distance = this.state.timeTill - timerNow;
// Time calculations for days, hours, minutes and seconds
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((distance % (1000 * 60)) / 1000);
//the variable displayed
var setTimer = days + "d " + hours + "h " + minutes + "m " + seconds + "s ";
this.setState({setTimer});
};

Upvotes: 0

Views: 1172

Answers (2)

Mor Shemesh
Mor Shemesh

Reputation: 2899

There's a few things you should do: 1. Have a method that will actually call setState 2. Change setInterval to call a method reference 3. Cleanup any pending request on unmounting: 4. [optional] you can drop that


private createTimer = { ... }

private setTimer = () => {
  this.setState({ timer: this.createTimer() });
}

private _mounted: boolean = false;
private _intervalHandler: number | null = null;

componentDidMount = async () => {

  this._mounted = true;

  this.someFunction().finally(() => {
    if (!this._mounted) { return; }

    this._intervalHandler = setInterval(this.setTimer, 1000);  
  });
}

componentWillUnmoun = () => {
  this._mounted = false;
  if (this._intervalHandler !== null) {
    clearInterval(this._intervalHandler);
  }
}

This will ensure you are not performing state changes after the component was unmounted.

Upvotes: 0

Asaf Aviv
Asaf Aviv

Reputation: 11800

You pass the returned value of createTimer to setInterval, instead pass the function but don't invoke it

setInterval(this.createTimer, 1000)

Upvotes: 1

Related Questions