Tell us what’s happening:
I’m currently working on displaying the right symbol to the button within the 3x3 grid (either ‘X’ or ‘O’ depending on the current player). However, I am utilizing a 2-D array to make things more flexible if I ever want to expand on this project in the future (e.g. make it 4x4 or 5x5). I was also told this makes it easier to keep track of positioning to make it easier to recognize a winner pattern.
At the current moment, upon clicking a grid-item it displays either ‘X’ or ‘O’ to the ENTIRE column rather than just that specific button. I have no idea why.
A quick follow-up question here, since this is a 2-D array what exactly should I be using for the key?
Your code so far
<!-- file: index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Tic-Tac-Toe</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { Board } from './index.jsx';
ReactDOM.createRoot(document.getElementById('root')).render(<Board />);
</script>
</body>
</html>
/* file: index.jsx */
const { useState } = React;
export function Board() {
const [currentPlayer, setCurrentPlayer] = useState("X");
const [boardSize, setBoardSize] = useState(3);
const [boxes, setBoxes] = useState(new Array(boardSize).fill(new Array(boardSize).fill(undefined)));
// const [boxes, setBoxes] = useState(new Array(boardSize).fill(undefined));
const [haveWinner, setHaveWinner] = useState(false);
const [winner, setWinner] = useState();
function handleMove(rowIndex, colIndex) {
const newBoxes = [...boxes];
if (currentPlayer === "X") {
newBoxes[rowIndex][colIndex] = "X";
setCurrentPlayer("O");
} else {
newBoxes[rowIndex][colIndex] = "O";
setCurrentPlayer("X");
}
setBoxes(newBoxes);
console.log(newBoxes);
handleResult();
}
function handleReset() {
const newBoxes = new Array(boardSize).fill(new Array(boardSize).fill(undefined))
setCurrentPlayer("X");
setBoxes(newBoxes);
setHaveWinner(false);
}
function handleResult() {
}
return (
<div className="main-container">
<h1>Tic-Tac-Toe</h1>
<h2>{haveWinner ? `Winner: ${winner}` : `Next Player: ${currentPlayer}`}</h2>
<div className="grid-container">
{
boxes.map((item, rowIndex) => (
item.map((subItem, colIndex) => (
<button key={colIndex} disabled={!!subItem} onClick={() => handleMove(rowIndex, colIndex)} role="button" className="square">{subItem}</button>
))))
}
</div>
<button onClick={handleReset} id="reset" role="button">Reset Game</button>
</div>
);
}
/* const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(arr[0][1]);
const newArr = arr.map(item => item.map(subItem => subItem + 1)); */
/* file: styles.css */
.main-container {
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
text-align: center;
}
h2 {
text-align: center;
font-weight: normal;
}
.grid-container {
display: grid;
grid-template-columns: auto auto auto;
grid-template-rows: auto auto auto;
padding: 5px;
gap: .5rem;
justify-content: center;
}
.square {
border: 1px solid black;
border-radius: 10px;
width: 5rem;
height: 5rem;
padding: 10px;
font-size: 30px;
text-align: center;
background-color: white;
}
.square:hover {
cursor: pointer;
}
#reset {
font-size: 20px;
border: 1px solid black;
border-radius: 5px;
margin-top: 2rem;
padding: 5px;
}
#reset:hover {
cursor: pointer;
}
#reset:active {
background-color: red;
}
Visual Reference:
Challenge Information:
Build a Tic-Tac-Toe Game - Build a Tic-Tac-Toe Game
