React.js - 如何停止setInterval并在单击后恢复它

时间:2018-02-07 08:15:18

标签: javascript reactjs ecmascript-6 setinterval mount

我正在使用React.js开发生命游戏。到目前为止一切都有效,但我想添加额外的功能,这将允许停止游戏(周期)并在需要时恢复它。我不知道怎么办呢。所以在主类中我有一个方法/函数来启动游戏,并在单击render()中定义的按钮后触发:

  constructor(props) {
    super(props);
    this.state = {cellStates: Array(TOTAL_FIELDS).fill(0), cellID: Array(TOTAL_FIELDS), gameRunning: false, counter: 0}; //set and fill initial states

    this.changeState = this.changeState.bind(this);

    this.resetGame = this.resetGame.bind(this);
    this.startGame = this.startGame.bind(this);
    this.stopGame = this.stopGame.bind(this);
    this.showGameStatus = this.showGameStatus.bind(this);

    for (let o = 1; o <= TOTAL_FIELDS; o++)
    {
      this.state.cellID[o-1] = o;
    }
  }

startGame() {
    function checkZeros(arr) { //check if board is blank
      return arr === 0;
    }

    if (!this.state.cellStates.every(checkZeros)) {
      alert ('Game starts!');
      let cycle = this.state.counter; //define number of life cycle

      this.setState(() => {
        return {
          gameRunning: true //game is on
        };
      });

      $('#reset-button').attr("disabled", "disabled");
      $('#start-button').attr("disabled", "disabled");
      $('#stop-button').removeAttr("disabled"); //handle buttons


      let goGame = setInterval(() => {
        const newStates = [];

        for (let x = 0; x < TOTAL_FIELDS; x++) {
          if (this.state.cellStates[x] === 0) {  //cell is dead
            if (this.calculateCellsAround(x) === 3)
            {
              newStates.push(1);
            }
            else {
              newStates.push(this.state.cellStates[x]);
            }
          }
          else {  //cell is alive
            if (this.calculateCellsAround(x) === 3 || this.calculateCellsAround(x) === 2)
            {
              newStates.push(this.state.cellStates[x]);
            }
            else {
              newStates.push(0);
            }
          }
        }
        this.setState(() => {
            return {cellStates: newStates, counter: cycle++};
        });
      }, 50);
    }
    else {
      alert ("Please fill at least one cell to launch a game.")
    }
  }

  render() {
    return (
      <div style={{marginTop:0+"px"}} key="wrap-it-all">
        <Header />
        <Grid>
          <Row>
            <Col md={12}>
              <div className="board-outline">
                {this.createMap(140, TOTAL_FIELDS)}
              </div>
            </Col>
          </Row>
          <Row>
            <Col md={12}>
              <button id="start-button" onClick={() => this.startGame()}>Start</button>
              <button id="stop-button" onClick={() => this.stopGame()}>Stop</button>
              <button id="reset-button" onClick={() => this.resetGame()}>Reset</button>
              <p style={{color:`#fff`}}>Lifecycle: <span className="lifecycle">{this.state.counter}</span>. Game is <span className="gamerun">{this.showGameStatus()}</span>.</p>
              <Alert className="disclaimer" bsStyle="warning">
                <strong>Instruction:</strong> Select the initial state of cells and press the [Start] button.
              </Alert>
            </Col>
          </Row>
        </Grid>
      </div>
    );
  }

我认为这一切与mount / unmount内置函数有关,但我不知道如何使用它们以使它全部工作。

3 个答案:

答案 0 :(得分:3)

您需要在停止时使用clearInterval(handle)取消间隔。 如果您想继续,请再次使用setInterval(callback, tick)

此外,只需将您的匿名函数(又名setInterval(() => {...})更改为

即可
function onTimerTick() {
   //copy everything marked as "..." here
}

并使用setInterval(onTimerTick, 50)

答案 1 :(得分:0)

您可以尝试将局部变量(让goGame)更改为全局变量(this._timer),然后可以通过clearInterval(this._timer)取消间隔

答案 2 :(得分:0)

您可以做这样的事情

  let intervalHandle: NodeJS.Timeout = null;

  const startIntervalTask = () => {
    intervalHandle = setInterval(() => {
      // execute this code after every 5000 miliseconds

    }, 5000);
  };

  const stopIntervalTask = ()=>{
    clearInterval(intervalHandle);    
  }