最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to run a while loop in a React Component while this.state=X - Stack Overflow

programmeradmin5浏览0评论

Basically I have a class called Game. Its state consists of a 2d array squares and a variant which just changes the color of a button between 'success' and 'danger'. Whenever I press the button, and switch the variant in the state to 'danger', I want the loop inside this.startGame() to repeat until I press the button and change it back. On each iteration of the loop in startGame(), it alters squares and calls this.setState to update squares. I have squares rendered on the screen, and I would like to see a rerender on each loop and the squares on the screen to be changing each time. Currently, when I press the button to start the loop, nothing happens; same when I press it again to end it. But if I remove the loop within startGame(), the code runs once and it actually does what it's supposed to do. Thing is, I want it to keep doing it (while this.state.variant == 'danger') until I press the button to stop it. What am I doing wrong and how can I fix it?

import React from 'react';
import Grid from './Grid';
import NavigationBar from './NavigationBar';
import Legend from './Legend';

class Game extends React.Component {
  constructor(props) {
    super(props);

    // initialize 2d array to store the state of the game
    let abc = Array(30);
    for(var i=0; i < abc.length; i++) {
      abc[i] = Array(79);
      for(var j=0; j < abc[i].length; j++) {
        abc[i][j] = 0;
      }
    }
    this.state = {
      squares: abc,
      start: 'Start Simulation!',
      variant: 'success',
    }
  }

  // change value at squares[x][y]
  handleClick(x, y) {
    const squares = this.state.squares.slice();
    squares[x][y] = squares[x][y] === 0 ? 1 : 0;
    this.setState({squares: squares});
  }

  handleClickNav(mand) {
    if(mand === 'clear') {
      // clears the grid of alive cells
      const temp = this.state.squares;
      for(var i=0; i < 30; i++) {
        for(var j=0; j < 79; j++) {
          temp[i][j] = 0;
        }
      }
      this.setState({sqares: temp});
    }
    // pressing the randomize button makes it so that each cell has a 1/10 probability to be alive, 1/10 so that there isn't a lot of clutter
    if(mand === 'randomize') {
      const values = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
      const temp = this.state.squares;
      for(var i=0; i < 30; i++) {
        for(var j=0; j < 79; j++) {
          temp[i][j] = values[Math.floor(Math.random() * 10)]
        }
      }
      this.setState({squares: temp});
    }
    // starts the game
    if(mand === 'start') {
      // change the start button to a stop button and vice versa
      if(this.state.variant === 'success') {
        this.setState({
          start: 'Stop Simulation',
          variant: 'danger',
        })
        this.startGame();
      }
      else {
        this.setState({
          start: 'Start Simulation!',
          variant: 'success',
        })
      }
    }


  }

  startGame() {
    while(this.state.variant === 'danger') {
      let neighbors = Array(30);
      for(var i = 0; i < neighbors.length; i++) {
        neighbors[i] = Array(79);
        for(var j = 0; j < neighbors[i].length; j++) {
          neighbors[i][j] = 0;
        }
      }

      for(var i = 0; i < 30; i++) {
        for(var j = 0; j < 79; j++) {
          // do the corner first
          if(i === 0 && j === 0) {
            neighbors[i][j] = this.state.squares[i+1][j] + this.state.squares[i][j+1] + this.state.squares[i+1][j+1];
          }
          else if(i === 0 && j === neighbors[0].length - 1) {
            neighbors[i][j] = this.state.squares[i][j-1] + this.state.squares[i+1][j-1] + this.state.squares[i+1][j];
          }
          else if(i === neighbors.length - 1 && j === 0) {
            neighbors[i][j] = this.state.squares[i-1][j] + this.state.squares[i-1][j+1] + this.state.squares[i][j+1];
          }
          else if(i === neighbors.length - 1 && j === neighbors[0].length - 1) {
            neighbors[i][j] = this.state.squares[i-1][j] + this.state.squares[i-1][j-1] + this.state.squares[i][j-1];
          }
          // now the edges
          else if(i === 0) {
            neighbors[i][j] = this.state.squares[i][j-1] + this.state.squares[i+1][j-1] + this.state.squares[i+1][j] + this.state.squares[i+1][j+1] + this.state.squares[i][j+1];
          }
          else if(i === neighbors.length - 1) {
            neighbors[i][j] = this.state.squares[i][j-1] + this.state.squares[i-1][j-1] + this.state.squares[i-1][j] + this.state.squares[i-1][j+1] + this.state.squares[i][j+1];
          }
          else if(j === 0) {
            neighbors[i][j] = this.state.squares[i-1][j] + this.state.squares[i-1][j+1] + this.state.squares[i][j+1] + this.state.squares[i+1][j+1] + this.state.squares[i+1][j];
          }
          else if(j === neighbors[0].length - 1) {
            neighbors[i][j] =  this.state.squares[i-1][j] + this.state.squares[i-1][j-1] + this.state.squares[i][j-1] + this.state.squares[i+1][j-1] + this.state.squares[i+1][j];
          }
          // general case
          else {
            neighbors[i][j] = this.state.squares[i-1][j-1] + this.state.squares[i-1][j] + this.state.squares[i-1][j+1] + this.state.squares[i][j+1] + 
                              this.state.squares[i+1][j+1] + this.state.squares[i+1][j] + this.state.squares[i+1][j-1] + this.state.squares[i][j-1];
          }
        }
      }

      let newArr = Array(30);
      for(var i = 0; i < newArr.length; i++) {
        newArr[i] = Array(79);
        for(var j = 0; j < newArr[i].length; j++) {
          if(this.state.squares[i][j] == 1) {
            if(neighbors[i][j] < 2) {
              newArr[i][j] = 0;
            }
            else if(neighbors[i][j] > 3) {
              newArr[i][j] = 0;
            }
            else {
              newArr[i][j] = 1;
            }
          }
          else {
            if(neighbors[i][j] == 3) {
              newArr[i][j] = 1;
            }
            else {
              newArr[i][j] = 0;
            }
          }
        }
      }
      this.setState({squares: newArr});
    }
  }

  render() {
    return (
      <div>
        <NavigationBar 
          onClick={(mand) => this.handleClickNav(mand)}
          start={this.state.start}
          variant={this.state.variant}
        />
        <Legend />
        <div className="game">
          <div className="game-grid">
            <Grid
              squares={this.state.squares}
              onClick={(x, y) => this.handleClick(x, y)}
            />
          </div>
        </div>
      </div>
    );
  }
}

export default Game;

Basically I have a class called Game. Its state consists of a 2d array squares and a variant which just changes the color of a button between 'success' and 'danger'. Whenever I press the button, and switch the variant in the state to 'danger', I want the loop inside this.startGame() to repeat until I press the button and change it back. On each iteration of the loop in startGame(), it alters squares and calls this.setState to update squares. I have squares rendered on the screen, and I would like to see a rerender on each loop and the squares on the screen to be changing each time. Currently, when I press the button to start the loop, nothing happens; same when I press it again to end it. But if I remove the loop within startGame(), the code runs once and it actually does what it's supposed to do. Thing is, I want it to keep doing it (while this.state.variant == 'danger') until I press the button to stop it. What am I doing wrong and how can I fix it?

import React from 'react';
import Grid from './Grid';
import NavigationBar from './NavigationBar';
import Legend from './Legend';

class Game extends React.Component {
  constructor(props) {
    super(props);

    // initialize 2d array to store the state of the game
    let abc = Array(30);
    for(var i=0; i < abc.length; i++) {
      abc[i] = Array(79);
      for(var j=0; j < abc[i].length; j++) {
        abc[i][j] = 0;
      }
    }
    this.state = {
      squares: abc,
      start: 'Start Simulation!',
      variant: 'success',
    }
  }

  // change value at squares[x][y]
  handleClick(x, y) {
    const squares = this.state.squares.slice();
    squares[x][y] = squares[x][y] === 0 ? 1 : 0;
    this.setState({squares: squares});
  }

  handleClickNav(mand) {
    if(mand === 'clear') {
      // clears the grid of alive cells
      const temp = this.state.squares;
      for(var i=0; i < 30; i++) {
        for(var j=0; j < 79; j++) {
          temp[i][j] = 0;
        }
      }
      this.setState({sqares: temp});
    }
    // pressing the randomize button makes it so that each cell has a 1/10 probability to be alive, 1/10 so that there isn't a lot of clutter
    if(mand === 'randomize') {
      const values = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
      const temp = this.state.squares;
      for(var i=0; i < 30; i++) {
        for(var j=0; j < 79; j++) {
          temp[i][j] = values[Math.floor(Math.random() * 10)]
        }
      }
      this.setState({squares: temp});
    }
    // starts the game
    if(mand === 'start') {
      // change the start button to a stop button and vice versa
      if(this.state.variant === 'success') {
        this.setState({
          start: 'Stop Simulation',
          variant: 'danger',
        })
        this.startGame();
      }
      else {
        this.setState({
          start: 'Start Simulation!',
          variant: 'success',
        })
      }
    }


  }

  startGame() {
    while(this.state.variant === 'danger') {
      let neighbors = Array(30);
      for(var i = 0; i < neighbors.length; i++) {
        neighbors[i] = Array(79);
        for(var j = 0; j < neighbors[i].length; j++) {
          neighbors[i][j] = 0;
        }
      }

      for(var i = 0; i < 30; i++) {
        for(var j = 0; j < 79; j++) {
          // do the corner first
          if(i === 0 && j === 0) {
            neighbors[i][j] = this.state.squares[i+1][j] + this.state.squares[i][j+1] + this.state.squares[i+1][j+1];
          }
          else if(i === 0 && j === neighbors[0].length - 1) {
            neighbors[i][j] = this.state.squares[i][j-1] + this.state.squares[i+1][j-1] + this.state.squares[i+1][j];
          }
          else if(i === neighbors.length - 1 && j === 0) {
            neighbors[i][j] = this.state.squares[i-1][j] + this.state.squares[i-1][j+1] + this.state.squares[i][j+1];
          }
          else if(i === neighbors.length - 1 && j === neighbors[0].length - 1) {
            neighbors[i][j] = this.state.squares[i-1][j] + this.state.squares[i-1][j-1] + this.state.squares[i][j-1];
          }
          // now the edges
          else if(i === 0) {
            neighbors[i][j] = this.state.squares[i][j-1] + this.state.squares[i+1][j-1] + this.state.squares[i+1][j] + this.state.squares[i+1][j+1] + this.state.squares[i][j+1];
          }
          else if(i === neighbors.length - 1) {
            neighbors[i][j] = this.state.squares[i][j-1] + this.state.squares[i-1][j-1] + this.state.squares[i-1][j] + this.state.squares[i-1][j+1] + this.state.squares[i][j+1];
          }
          else if(j === 0) {
            neighbors[i][j] = this.state.squares[i-1][j] + this.state.squares[i-1][j+1] + this.state.squares[i][j+1] + this.state.squares[i+1][j+1] + this.state.squares[i+1][j];
          }
          else if(j === neighbors[0].length - 1) {
            neighbors[i][j] =  this.state.squares[i-1][j] + this.state.squares[i-1][j-1] + this.state.squares[i][j-1] + this.state.squares[i+1][j-1] + this.state.squares[i+1][j];
          }
          // general case
          else {
            neighbors[i][j] = this.state.squares[i-1][j-1] + this.state.squares[i-1][j] + this.state.squares[i-1][j+1] + this.state.squares[i][j+1] + 
                              this.state.squares[i+1][j+1] + this.state.squares[i+1][j] + this.state.squares[i+1][j-1] + this.state.squares[i][j-1];
          }
        }
      }

      let newArr = Array(30);
      for(var i = 0; i < newArr.length; i++) {
        newArr[i] = Array(79);
        for(var j = 0; j < newArr[i].length; j++) {
          if(this.state.squares[i][j] == 1) {
            if(neighbors[i][j] < 2) {
              newArr[i][j] = 0;
            }
            else if(neighbors[i][j] > 3) {
              newArr[i][j] = 0;
            }
            else {
              newArr[i][j] = 1;
            }
          }
          else {
            if(neighbors[i][j] == 3) {
              newArr[i][j] = 1;
            }
            else {
              newArr[i][j] = 0;
            }
          }
        }
      }
      this.setState({squares: newArr});
    }
  }

  render() {
    return (
      <div>
        <NavigationBar 
          onClick={(mand) => this.handleClickNav(mand)}
          start={this.state.start}
          variant={this.state.variant}
        />
        <Legend />
        <div className="game">
          <div className="game-grid">
            <Grid
              squares={this.state.squares}
              onClick={(x, y) => this.handleClick(x, y)}
            />
          </div>
        </div>
      </div>
    );
  }
}

export default Game;
Share Improve this question asked Nov 12, 2019 at 5:02 slowhandslowhand 751 gold badge2 silver badges5 bronze badges 0
Add a ment  | 

1 Answer 1

Reset to default 3

This is due to the fact the loop inside startGame() is a sync while-loop, yet react setState() is an async job. Once you kick start the sync loop, before it done looping, any async job just don't have a chance to step in. In other word, it just won't stop. You saw no change because you stuck in an infinite loop.

The trick to make your code work is to turn your while-loop into a async one, using setTimeout(). It goes like:

startGame() {
    const loop = () => {
        // change `while` to `if`
-       while (this.state.variant === 'danger') {
+       if (this.state.variant === 'danger') {
           // ...same code in previous while loop here
        }

        // at the end, start next async loop if condition still hold true
        if (this.state.variant === 'danger') setTimeout(loop, 0);
    }

    // kickstart the loop
    loop();
}

Now the loop is async too, so react setState() has a chance to step in and change value of this.state.variant, thus you can stop/start the loop by clicking button.

发布评论

评论列表(0)

  1. 暂无评论