setInterval参数值更新后clearInterval不清除

clearInterval not clearing after setInterval parameter value is updated

在这个游戏中(从 Youtube 上获得)蛇在进食时会长大,而且速度也会加快。一切正常,直到蛇至少吃了一顿饭。此时暂停、播放按钮根本不起作用,新游戏按钮重置蛇的位置和食物位置,但游戏不会停止,速度也不会重置。

我知道问题出在哪里 (increaseSpeed()) 但我不知道如何修复它。如果我从此函数中删除 setInterval return,游戏的每个部分都可以正常工作,除了速度永远不会改变

const getRandomCoordinates = () => {
  let min = 1
  let max = 98
  let x = Math.floor((Math.random()*(max-min+1)+min)/2)*2
  let y = Math.floor((Math.random()*(max-min+1)+min)/2)*2
  return [x,y]
}

const initialState = {
  direction: 'RIGHT',
  speed: 200,
  food: getRandomCoordinates(),
  snakeDots: [
    [0,0],
    [2,0]
  ],
  message: ""
}

// GAMEBOARD COMPONENT
class GameBoard extends Component {

  state = initialState

  componentDidMount() {
    document.onkeydown = this.onKeyDown
  }

  componentDidUpdate() {
    this.checkIfOutOfBorders()
    this.checkIfCollapsed()
    this.checkIfEat()
  }

  playButton = () => {
    clearInterval(this.intervalId)
    this.intervalId = setInterval(this.moveSnake, this.state.speed)
  }

  pauseButton = () => {
    clearInterval(this.intervalId)
  }

  newGameButton = () => {
    this.setState(initialState)
    clearInterval(this.intervalId)
  }


  onKeyDown = (e) => {
    e = e || window.event;
    switch(e.keyCode) {
      case 38:
        this.setState({ direction: 'UP'})
        break
      case 40:
        this.setState({ direction: 'DOWN'})
        break
      case 37:
        this.setState({ direction: 'LEFT'})
        break
      case 39:
        this.setState({ direction: 'RIGHT'})
        break
      default:
    }
  }

  moveSnake = () => {
    let dots = [...this.state.snakeDots]
    let head = dots[dots.length - 1]

    switch (this.state.direction) {
      case 'RIGHT':
        head = [head[0] + 2, head[1]]
        break
      case 'LEFT':
        head = [head[0] - 2, head[1]]
        break
      case 'UP':
        head = [head[0], head[1] - 2]
        break
      case 'DOWN':
        head = [head[0], head[1] + 2]
        break
      default:
    }
    dots.push(head)
    dots.shift()
    this.setState({
      snakeDots: dots
    })
  }

  checkIfOutOfBorders = () => {
    let head = this.state.snakeDots[this.state.snakeDots.length - 1]
    if (head[0] >= 100 || head[1] >= 100 || head[0] < 0 || head[1] < 0) {
      this.onGameOver()
    }
  }

  checkIfCollapsed = () => {
    let snake = [...this.state.snakeDots]
    let head = snake[this.state.snakeDots.length - 1]
    snake.pop()
    snake.forEach(dot => {
      if (head[0] === dot[0] && head[1] === dot[1]) {
        this.onGameOver()
      }
    })
  }

  checkIfEat = () => {
    let head = this.state.snakeDots[this.state.snakeDots.length - 1]
    let food = this.state.food
    if (head[0] === food[0] && head[1] === food[1]) {
      this.setState({
        food: getRandomCoordinates()
      })
      this.growSnake()
      this.increaseSpeed()
    }
  }

  growSnake = () => {
    let newSnake = [...this.state.snakeDots]
    newSnake.unshift([])
    this.setState({
      snakeDots: newSnake
    })
  }

  increaseSpeed = () =>  {
    let speed = this.state.speed
    if (this.state.speed > 10) {
      this.setState({
        speed: speed - 10
      })
    }
    setInterval(this.moveSnake, this.state.speed)
  }

  onGameOver = () => {
    // alert(`Game over! You're snake length is ${this.state.snakeDots.length}`)
    this.newGameButton()
    console.log('over')

  }

  render () {
    return (
      <div className="console-container">
        <div className="game-area">
          <Snake snakeDots={ this.state.snakeDots } />
          <Food food={ this.state.food } />
        </div>
        <ControlPanel
          playButton={this.playButton}
          pauseButton={this.pauseButton}
          newGameButton={this.newGameButton}
          scoreboardMessage={this.scoreboardMessage}
        />
      </div>
    )
  }
}

export default GameBoard

这一行的 increaseSpeed 方法有 3 个问题:

setInterval(this.moveSnake, this.state.speed);

  1. 您没有保存新的间隔 ID
  2. 您使用旧状态值(因为 setState 是异步的)
  3. 您没有清除之前的区间

这应该有效:

  increaseSpeed = () =>  {
    let speed = this.state.speed
    if (this.state.speed > 10) {
      speed -= 10;
      this.setState({
        speed
      })
    }
    clearInterval(this.intervalId);
    this.intervalId = setInterval(this.moveSnake, speed);
  }