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

What do you think these 4 lines are doing?

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 @RandellDawson 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 @RandellDawson 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 @RandellDawson.

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

@jawaka72 - Try commenting out those 4 lines and see if you see anything change in your app.

2 Likes

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.

You were trying to define a property named bind on this and assign it a bound method. You never reference this.bind anywhere else in your code, which is why those 4 lines were not doing anything. If you were to have called this.bind anywhere in your code, then only the 4th assignment would have stuck.

You were doing it correctly in the following two lines:

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

You actually do reference these elsewhere in your code, which is why they work.

1 Like

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.