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
1 Answer
Reset to default 3This 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.