I'm still learning React and working on coding John Conway's Game of Life. I have created a game board for the game using a 2D array in state. I have this 2D array stored in state in my App.js
. I also have two functional components, Gameboard.js
and Square.js
receiving props and creating the game board.
Gameboard.js
creates a table and passed props to Square.js
letting it know which squares should be highlighted and which shouldn't. In other words it let's each individual square know if the user has clicked it and if it's "alive" in the Game of Life sense.
After a user clicks a specific square in Square.js
I have a callback function triggered onClick. The callback function, aliveCallback
, passes the X and Y coordinates of the specific square that was clicked back to App.js
. App.js
triggers a function called alive
.
When alive
is called I need to update the state of the 2D array so that the specific square (it's X and Y coordinates, for example [0, 0] or [0, 1] etc...) is set to the boolean stored in state. Then, this boolean can be passed to Gameboard.js
as isAlive
. The game board then passes that isAlive
value to Square.js
and Square.js
has conditional rendering that tells that individual square to be highlighted light blue.
This has been a mouth full and it would probably be easiest to check out the Codesandbox and see the structure of the project. It also helps to mention that I am following advice a SO poster gave me in this post.
After doing some reading and looking at the React docs it appears a good approach to this issue is to use immutability-helper and it's update
function. That's the approach I have taken but I can't seem to properly update the state of the specific square clicked using it's X and Y coordinates.
Instead of posting code here it would probably just be easier and neater to check out the code on CodeSandbox. How can I write my alive
function so that it properly updates the state of whatever square is clicked on?
Here is what I've tried:
alive = (x, y) => {
console.log(x, y);
const data = this.state.board;
const ySquare = y;
const xSquare = x;
console.log("X square is : " + xSquare);
console.log("Y square is : " + ySquare);
const updateData = update(data, {
xSquare: {$set: this.setState({ alive: !this.state.alive })}
});
}
The logic here is to try to pass xSquare and ySquare to update
and then try to set alive to true for that particular square.
I think the problem is that you have a state variable alive
on the App which is being confused for the life status of each cell.
I've reworked things a bit, and my solution does not use update from 'immutability-helper'
since I'm not familiar with it, but the logic in my solution is pretty straightforward I think.
Here is App.alive
:
alive = (x, y) => {
console.log(x, y);
const data = this.state.board;
const ySquare = y;
const xSquare = x;
console.log("X square is : " + xSquare);
console.log("Y square is : " + ySquare);
const newData = data.map((row, y) => {
return y === ySquare ? (
row.map((cell, x) => x === xSquare ? 1 : cell)
) : (
row
)
})
this.setState({ board: newData });
}
Basically newData
comes from going over each cell in the board and if the current cell's coordinates match xSquare, ySquare
it sets the value of the cell to 1
, otherwise it leaves it unchanged.
As an additional note, I'd recommend renaming this method to something like makeCellAlive
just to be more clear that it is a method that does something.
Edit:
If you wanted to have click either make the cell alive or dead depending on it's current state, you could change the line:
row.map((cell, x) => x === xSquare ? 1 : cell)
to
row.map((cell, x) => x === xSquare ? (cell + 1) % 2 : cell)
Looks like a cool app btw.