game of life project in react, trying to change cell color onClick but it just won't work; I literally have no clue why not. stack overflow seems to want me to be more specific but I literally have no clue what the issue could be; it is referencing my array coordinates just fine(console.log confirmed) but it tells me that theres a type error on the y axis grid[x][y] and I dont know whether the type error is referring to the value at that index or whether it is trying to use the index itself; ive just been staring at it forever and I cant figure it out.
My canvas component(havent begun to split it down just yet; not commented too well yet)
import { useRef, useEffect } from "react";
export default function GameOfLifeCanvas() {
//! component wide variables
//! create reference to canvas
const canvasRef = useRef(null);
const resolution = 20;
const width = 400;
const height = 400;
const cellTypes = [
{
name: "standardCell",
liveRepresentation: 1,
color: "",
behavior: {},
},
];
//! cellTypeCount is the number of cell types; alive, x, y, z = 4;; + 1 for dead cell
const cellTypeCount = cellTypes.length + 1;
//! run when component mounts
useEffect(() => {
const columns = width / resolution;
const rows = height / resolution;
const canvas = canvasRef.current;
const context = canvas.getContext("2d");
let grid = () => {
//! start with empty array
const grd = [];
//! for every row
for (let i = 0; i < rows; i++) {
const row = [];
//! for every column
for (let ii = 0; ii < columns; ii++) {
//! push 0 to row
row.push(Math.floor(Math.random() * cellTypeCount));
}
//! push row to columns for every column
grd.push(row);
}
return grd;
};
function mouseClick(e) {
let mouseX, mouseY;
if (e.offsetX) {
mouseX = e.offsetX;
mouseY = e.offsetY;
} else if (e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}
let gridX = Math.floor(mouseX / resolution);
let gridY = Math.floor(mouseY / resolution);
console.log(gridX, gridY);
let xy = grid[gridX][gridY];
if (xy == 0) {
grid[gridX][gridY] = 1;
console.log("white");
} else if (xy === 1) {
grid[gridX][gridY] = 0;
console.log("black");
}
render(grid);
}
canvas.addEventListener("mousedown", mouseClick, false);
render(grid());
function render(grid) {
for (let col = 0; col < grid.length; col++) {
for (let row = 0; row < grid[col].length; row++) {
const cell = grid[col][row];
context.beginPath();
// context.rect(
// col * resolution,
// row * resolution,
// resolution,
// resolution
// );
//! truthy is black falsy is white
context.fillStyle = cell ? "black" : "white";
context.fillRect(
col * resolution,
row * resolution,
resolution,
resolution
);
context.stroke();
}
}
console.log(grid);
}
}, []);
return (
<>
<canvas ref={canvasRef} width={width} height={height}></canvas>
</>
);
}
In order to initialise the grid matrix to its initial state, you have created a function called grid. In the first render, this function is being called and its return value passed to the render function.
However, any time user clicks on the canvas and the grid needs to be updated, you seem to be modifying the function instead of its return value as
grid[gridX][gridY] = 1;
Instead, you should be storing the matrix of the grid in a variable, and operating on it.
In the code snippet below, I have placed your grid
function inside a global create_empty_grid
function. During the first render of the component, this function is called and the return value is stored in a variable. And on mousedown events, we modify the variable and re-render the canvas using it.
import { useRef, useEffect } from "react";
const resolution = 20;
const width = 400;
const height = 400;
const cellTypes = [
{
name: "standardCell",
liveRepresentation: 1,
color: "",
behavior: {},
},
];
const create_empty_grid = () => {
const columns = width / resolution;
const rows = height / resolution;
//! cellTypeCount is the number of cell types; alive, x, y, z = 4;; + 1 for dead cell
const cellTypeCount = cellTypes.length + 1;
//! start with empty array
const grd = [];
//! for every row
for (let i = 0; i < rows; i++) {
const row = [];
//! for every column
for (let ii = 0; ii < columns; ii++) {
//! push 0 to row
row.push(Math.floor(Math.random() * cellTypeCount));
}
//! push row to columns for every column
grd.push(row);
}
return grd;
};
export default function GameOfLifeCanvas() {
//! component wide variables
//! create reference to canvas
const canvasRef = useRef(null);
//! run when component mounts
useEffect(() => {
const canvas = canvasRef.current;
const context = canvas.getContext("2d");
let grid = create_empty_grid();
function mouseClick(e) {
let mouseX, mouseY;
if (e.offsetX) {
mouseX = e.offsetX;
mouseY = e.offsetY;
} else if (e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}
let gridX = Math.floor(mouseX / resolution);
let gridY = Math.floor(mouseY / resolution);
console.log(gridX, gridY);
let xy = grid[gridX][gridY];
if (xy == 0) {
grid[gridX][gridY] = 1;
console.log("white");
} else if (xy === 1) {
grid[gridX][gridY] = 0;
console.log("black");
}
render(grid);
}
canvas.addEventListener("mousedown", mouseClick, false);
render(grid);
function render(grid) {
for (let col = 0; col < grid.length; col++) {
for (let row = 0; row < grid[col].length; row++) {
const cell = grid[col][row];
context.beginPath();
//! truthy is black falsy is white
context.fillStyle = cell ? "black" : "white";
context.fillRect(
col * resolution,
row * resolution,
resolution,
resolution
);
context.stroke();
}
}
console.log(grid);
}
}, []);
return <canvas ref={canvasRef} width={width} height={height}></canvas>;
}