Personal Project Help: Resetting setInterval after clearing it

In this game I’m building, I clear the setInterval after the user loses. When they click Play Again? I want the timer to start again, but I’m having trouble making that happen while using React. I’ve tried several things like separating the timer into its own component, making a helper function, and using the life cycle methods, but I just can’t seem to get this part. I can start and clear the setInterval just fine, but it’s restarting it that’s my problem here. Any help is appreciated.

import React, {Component} from 'react';
// helper function to set a random action
function setRandomAction() {
  let actions = ['bop it', 'pull it', 'twist it'];
  let rando = actions[Math.floor(Math.random() * actions.length)];

  return rando;
}

class BopIt extends Component {
  constructor(props) {
    super(props);
    // set initial action in this.state so it is not empty on pageload
    this.state = {
      action: setRandomAction(),
      countdown: 3,
      userPressed: '',
      play: true
    }
    this.bind = this.keyPressed.bind(this);
    this.bind = this.keepPlaying.bind(this);
    this.bind = this.timer.bind(this);
    this.bind = this.endGame.bind(this);
    this.quitGame = this.quitGame.bind(this);
    this.playAgain = this.playAgain.bind(this);
  }
  componentDidMount() {
    let setTimerTime = parseInt(`${this.state.countdown - 2}000`);
    this.stopIntervalId = setInterval(() => this.timer(), setTimerTime);
    this.keyPressed();
  }

  componentWillUnmount() {
    this.keyPressed();
    this.keepPlaying();
    this.endGame();
  }

  timer() {
    let count = this.state.countdown;
    if (count === 0) {
      count = 4
    }
    this.setState({countdown: count - 1});
  }
  keyPressed() {
    document.addEventListener('keyup', (e) => {
      if (e.key === 'ArrowLeft') {
        this.setState({
          userPressed: 'pull it'
        });
      } else if (e.key === 'ArrowDown') {
        this.setState({
          userPressed: 'bop it'
        });
      } else if (e.key === 'ArrowRight') {
        this.setState({
          userPressed: 'twist it'
        });
      } else {
        this.setState({
          userPressed: 'wrong'
        });
      }
      if (this.state.userPressed !== this.state.action) {
        this.endGame();
      } else {
        this.keepPlaying();
      }
    });
  }

  keepPlaying() {
    let actions = ['bop it', 'pull it', 'twist it'];
    let rando = actions[Math.floor(Math.random() * actions.length)];
    this.setState({
      action: rando,
      userPressed: ''
    });
  }
  endGame() {
    console.log('You Lost!!!');
    this.setState({
      play: false
    });
    clearInterval(this.stopIntervalId);
  }
  quitGame() {
    clearInterval(this.stopIntervalId);
  }
  playAgain() {
    this.setState({
      play: true,
      action: setRandomAction(),
      countdown: 3
    });
  }
  render() {
    // if (this.state.countdown <= 0) {
    //   this.endGame();
    // }
    console.log(this.state)

    let gameAction = `${this.state.action} ${this.state.countdown}`;
    return (
      <div className="bop-it">
        <div className="show-action">
        {this.state.play ?  gameAction : <ResetGame playAgain={this.playAgain} quitGame={this.quitGame}/> }
        </div>
        <span>Pull It</span>
          <br/>
        <span>Bop It</span>
          <br/>
        <span>Twist It</span>
      </div>
    );
  }
}


class ResetGame extends Component {
  render() {
    return (
      <div>
        <input type="button" value="Play Again?" onClick={this.props.playAgain}/>
        <input type="button" value="Quit Game?" onClick={this.props.quitGame}/>
      </div>
    );
  }
}

export default BopIt

These four lines are binding their respective methods to the BopIt component. This is one way to bind methods to a component in React.

I got it to work now, here’s my updated code:

import React, {Component} from 'react';
// helper function to set a random action
function setRandomAction() {
  let actions = ['bop it', 'pull it', 'twist it'];
  let rando = actions[Math.floor(Math.random() * actions.length)];

  return rando;
}

class BopIt extends Component {
  constructor(props) {
    super(props);
    // set initial action in this.state so it is not empty on pageload
    this.state = {
      action: setRandomAction(),
      countdown: 3,
      userPressed: '',
      play: true
    }
    this.bind = this.keyPressed.bind(this);
    this.bind = this.keepPlaying.bind(this);
    this.bind = this.endGame.bind(this);
    this.bind = this.timer.bind(this);
    this.bind = this.startTimer.bind(this);
    this.quitGame = this.quitGame.bind(this);
    this.playAgain = this.playAgain.bind(this);
  }
  componentDidMount() {
    this.keyPressed();
    this.startTimer();
  }
  startTimer() {
    this.stopIntervalId = setInterval(() => this.timer(), 1000);
  }
  componentWillUnmount() {
    this.keyPressed();
    this.keepPlaying();
    this.endGame();
  }
  timer() {
    let count = this.state.countdown;
    if (count === 0) {
      count = 4
    }
    this.setState({countdown: count - 1});
  }
  keyPressed() {
    document.addEventListener('keyup', (e) => {
      if (e.key === 'ArrowLeft') {
        this.setState({
          userPressed: 'pull it'
        });
      } else if (e.key === 'ArrowDown') {
        this.setState({
          userPressed: 'bop it'
        });
      } else if (e.key === 'ArrowRight') {
        this.setState({
          userPressed: 'twist it'
        });
      } else {
        this.setState({
          userPressed: 'wrong'
        });
      }
      if (this.state.userPressed !== this.state.action) {
        this.endGame();
      } else {
        this.keepPlaying();
      }
    });
  }

  keepPlaying() {
    this.setState({
      action: setRandomAction(),
      countdown: 3,
      userPressed: ''
    });
  }
  endGame() {
    console.log('You Lost!!!');
    this.setState({
      play: false
    });
    clearInterval(this.stopIntervalId);
  }
  quitGame() {
    // clearInterval(this.stopIntervalId);
    console.log('you have left the game')
  }
  playAgain() {
    this.setState({
      play: true,
      action: setRandomAction(),
      countdown: 3
    });
    this.startTimer();
  }
  render() {
    // if (this.state.countdown <= 0) {
    //   this.endGame();
    // }
    // console.log(this.state)

    let gameAction = `${this.state.action} ${this.state.countdown}`;
    return (
      <div className="bop-it">
        <div className="show-action">
        {this.state.play ?  gameAction :
          <ResetGame
            playAgain={this.playAgain}
            quitGame={this.quitGame}
          />
        }
        </div>
        <span>Pull It</span>
          <br/>
        <span>Bop It</span>
          <br/>
        <span>Twist It</span>
      </div>
    );
  }
}


class ResetGame extends Component {
  render() {
    return (
      <div>
        <input type="button" value="Play Again?" onClick={this.props.playAgain}/>
        <input type="button" value="Quit Game?" onClick={this.props.quitGame}/>
      </div>
    );
  }
}

export default BopIt

What i’ve done differently is add a startTimer method to the BopIt component. I then call this method in componentDidMount() so it is fired the first time the game is run, then I call it again in the playAgain() method. I thought I did this exact same thing a while ago when I was trying to figure this out, but I must have done something wrong then. Anyways, now startTimer() controls whether or not setInterval should be run.

Hey @jawaka72,
I disagree with your assessment about the 4 lines @camperextraordinaire is pointing out. Glad to hear your setInterval is working.

Hi @Ethanefung
Would it be more accurate to say that those 4 lines represent how to attach eventHandlers to a component in React? The whole purpose of this personal project is for me to learn React. So, if you have any explanation of what @camperextraordinaire was pointing out, by all means share. I’m here to learn!
And thank you for your candidness.

I think this is not so much a React issue as it is a Javascript issue.

Try running the following code, and solidifying an explanation. Then refer back to the four lines pointed out by @camperextraordinaire.

const obj = {}

obj.reference = 'hi';
console.log(obj.reference);
obj.reference = 'hello';
console.log(obj.reference);

compared to the four lines

this.bind = this.keyPressed.bind(this);
this.bind = this.keepPlaying.bind(this);
this.bind = this.timer.bind(this);
this.bind = this.endGame.bind(this);
1 Like

So, the only method that is actually binding is the last one because I’m just overwriting the previous methods by applying this.bind = this.whatever.bind(this);?
I just ran this in my code

console.log(this)
    this.bind = this.keyPressed.bind(this);
    console.log(this)
    this.bind = this.keepPlaying.bind(this);
    console.log(this)
    this.bind = this.endGame.bind(this);
    console.log(this)
    this.bind = this.timer.bind(this);
    console.log(this)
    this.bind = this.startTimer.bind(this);

and the name of the bind method in all of the console.logs is bound startTimer. Only the final method is attached to the React component.
I thought that this was how a component’s methods were built in React.

1 Like

I just commented them out and the app works fine. I thought that this was how methods were created in React, but that is not the case.

Yes, I definitely see what you mean now. I’m now looking more closely at what I’m logging from before and I can clearly see

    this.playAgain = this.playAgain.bind(this);

are attached as their own methods to the component object. Now, I’m starting to wonder if I even need all of these methods to be bound like this in the first place. I’ll have to dig through the React documentation to be sure.