admin管理员组

文章数量:1401207

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.

本文标签: javascriptHow to run a while loop in a React Component while thisstateXStack Overflow