I’m learning React by following the tic-tac-toe exercise shown in this page. In the Wrapping up chapter, one of the extra assignments is:
Rewrite Board to use two loops to make the squares instead of hardcoding them.
So I went from this:
function Board({ xIsNext, squares, onPlay }) {
function handleClick(i) {
if (calculateWinner(squares) || squares[i]) {
return;
}
const nextSquares = squares.slice();
if (xIsNext) {
nextSquares[i] = 'X';
} else {
nextSquares[i] = 'O';
}
onPlay(nextSquares);
}
const winner = calculateWinner(squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (xIsNext ? 'X' : 'O');
}
return (
<>
<div className="status">{status}</div>
<div className="board-row">
<Square value={squares[0]} onSquareClick={() => handleClick(0)} />
<Square value={squares[1]} onSquareClick={() => handleClick(1)} />
<Square value={squares[2]} onSquareClick={() => handleClick(2)} />
</div>
<div className="board-row">
<Square value={squares[3]} onSquareClick={() => handleClick(3)} />
<Square value={squares[4]} onSquareClick={() => handleClick(4)} />
<Square value={squares[5]} onSquareClick={() => handleClick(5)} />
</div>
<div className="board-row">
<Square value={squares[6]} onSquareClick={() => handleClick(6)} />
<Square value={squares[7]} onSquareClick={() => handleClick(7)} />
<Square value={squares[8]} onSquareClick={() => handleClick(8)} />
</div>
</>
);
}
To this, see the changes comment for the difference:
function Board({ xIsNext, squares, onPlay }) {
function handleClick(i) {
if (squares[i] || calculateWinner(squares)) {
return;
}
const nextSquares = squares.slice();
if (xIsNext) {
nextSquares[i] = "X";
}
else {
nextSquares[i] = "O";
}
onPlay(nextSquares);
}
const winner = calculateWinner(squares);
let status;
if (winner) {
status = "Winner: " + winner;
}
else {
status = "Next player: " + (xIsNext ? "X" : "O");
}
/* CHANGES START HERE */
let content = [];
let n=0;
for (let i=0; i<3; i++) {
let row = [];
for (let j=0; j<3; j++) {
row.push(<Square key={"square-" + n} value={squares[n]} onSquareClick={() => handleClick(n)} />);
n++;
}
content.push(<div key={"row-" + i} className="board-row">{row}</div>);
}
return (
<>
<div className="status">{status}</div>
{content}
</>
);
}
The Board is correctly drawn and also the keys for the elements in the array are generated correctly as requested by the compiler:
However, if I click on a square, the cell doesn’t get the X/O sign as it should, also the game tracker just logs the first move and ignores any subsequent click on other squares.
I probably would have used a .map() as the main docs suggest, but since the assignment specifically mentions two loops, I don’t know what I’m doing wrong.
I also took a look at this answer, but I have extra data I wouldn’t know where to place, such as the className and keys.
Thanks in advance for your help!
>Solution :
I believe that the problem is not related to react, it’s just that you declared your n variable before the loops, so it has always the latest value (which is 9)
Your code simulation (click any number):
let n = 0
for (let i = 0; i < 3; i += 1) {
for (let j = 0; j < 3; j += 1) {
const el = document.createElement('div')
el.textContent = n
el.addEventListener('click', () => handleClick(n))
document.body.appendChild(el)
n += 1
}
}
function handleClick(n) {
console.log(n)
}
You should calculate n based on i and j instead
Fixex:
for (let i = 0; i < 3; i += 1) {
for (let j = 0; j < 3; j += 1) {
const n = i * 3 + j
const el = document.createElement('div')
el.textContent = n
el.addEventListener('click', () => handleClick(n))
document.body.appendChild(el)
}
}
function handleClick(n) {
console.log(n)
}

