Search code examples
reactjssetintervalcountdown

Doing a countdown in React


I'm trying to work out how to do a pre-game countdown in a Game component in React. I'm trying like this, but it's saying that this.countDown isn't a function - when it's clearly defined.

Can someone point out where I'm going wrong?

class Game extends Component {
  constructor() {
    super();

    this.state = {
      timer: 5,
      isHidden: true,
      randomIndex: 0,
      score: 0
    };

    this.countDown = this.countDown.bind(this);
  }

  countDown() {
    this.setState({
      timer: this.state.timer--
    });
  }

  render() {
    const tables = this.props.tables.map(table => {
      return (
        <li key={table.uniqueId}>
          {table.timesTable[0]} X {table.timesTable[1]}
        </li>
      );
    });

    setInterval(function() {
      this.countDown();
      console.log(this.state.timer);
    }, 1000);

    // if (this.state.timer > 0) {
    //     this.countDown();
    // }

    return (
      <div className="Game">
        <h3>Current Tables are:</h3>
        {tables}
        <h1 id="countdown">{this.state.timer}</h1>
        <Question />
        {/* question handles the right or wrong logic, receives a random timestable */}
        <h3>Score: {this.state.score}</h3>
        <button onClick={this.stopGame}>Stop Game</button>
        <button onClick={this.startOver}>Start Over</button>
      </div>
    );
  }
}

export default Game;

Solution

  • In that example, this in setInterval's callback refers to the global window object, since it's executed as window.setInterval(...), so this.countDown() would be equal to window.countDown(), which is obviously incorrect.

    To get this from parent's scope, you could use arrow functions.

    setInterval(() => {
      this.countDown();
      console.log(this.state.timer)
    }, 1000);
    

    or simply bind this:

    setInterval(function() {
      this.countDown();
      console.log(this.state.timer)
    }.bind(this), 1000); // bind this from parent's scope