Issue
I am working on a simple testing app using NextJS/React, and I have a question at the end.
Below is the code:
The Board.tsx file content:
'use client'
import './Board.css';
import {useState} from 'react';
export default function Board({count}:{count:number}) {
const [cellLights,setCellLights] = useState(Array.from({length:0},()=>
Array.from({length:0},()=>false)));
const refresh = () => {
let tempLights:boolean[][] = cellLights;
for (let idx=0; idx<tempLights.length; idx++) {
for (let idix=0; idix<tempLights[idx].length; idix++) {
tempLights[idx][idix] = false;
}
}
setCellLights(tempLights)
} /* End of refresh */
let rows:number[] = [];
while (rows.length<count) {rows.push(rows.length)}
let tempLights:boolean[][] = [];
rows.forEach(rowElt => {
let rowLights:boolean[] = [];
rows.forEach(colElt => {
rowLights.push(false)
});
tempLights.push(rowLights)
});
if (tempLights.length!=cellLights.length) setCellLights(tempLights)
return <div className="board">
{
rows.map(item => (
<div key={item}>
<Row count={count}
flags={cellLights[item]}
cleanFn={refresh}/>
</div>
))
}
</div>
} /* End of Board */
function Row({count,flags,cleanFn}:{count:number,flags:boolean[],cleanFn:()=>void}) {
let cells:number[] = [];
while (cells.length<count) {cells.push(cells.length)}
return <div className="row">
{
cells.map(item => (
<div key={item}>
<Cell flag={flags[item]}
cleanFn={cleanFn}/>
</div>
))
}
</div>
} /* End of Row */
function Cell({flag,cleanFn}:{flag:boolean,cleanFn:()=>void}) {
const [high, setHighFlag] = useState(flag);
function cellClick() {
cleanFn();
setHighFlag(!high)
}
return <>
{(!high) && <div className="cell" onClick={cellClick}/>}
{(high) && <div className="cell high" onClick={cellClick}/>}
</>
} /* End of Cell */
And the Board.css file content:
.cell {
width: 3rem;
height: 3rem;
background-color: rgb(255, 255, 0);
border-width: 2px;
border-color: rgb(55, 55, 55);
margin:0.1rem;
}
.high {
background-color: rgb(255, 0, 0);
}
.row {
display: flex;
flex-direction: row;
}
.board {
display: flex;
flex-direction: column;
}
At the top is the app/page.tsx file:
import Image from 'next/image'
import Board from './components/Board'
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
{/* // <main> */}
X4C APP
<Board count={3}/>
</main>
)
}
Here is what appears in the web browser when running the app, after clicking two square cells:
Now comes my question. One can see two cells highlighted in red. But this is not what I expect from my code.
Since when a click is performed, a call to cleanFn() -consequently to refresh()- is made, I would expect the previously highlighted cell to come back to its original color (yellow) before the last clicked cell becomes red.
Seeing the screenshot, this is obviously not what happens. Can anyone see where the flaw is in the code?
Solution
There is too much duplication of the cellLights
array data. In fact, the code is recomputing the array data and passing extra data down as props. The issue is that the Cell
component doesn't reset its state when the Board
component's cellLights
state updates.
Update the code so the cellLights
is the single source of truth for which cell is highlighted.
Board
function Board({ count }: { count: number }) {
// Initialize to count x count board
const [cellLights, setCellLights] = useState(
Array.from({ length: count }, () =>
Array.from({ length: count }, () => false)
)
);
const toggleCell = (row: number, col: number) => {
// Reset board, toggle specific cell true
setCellLights(
Array.from({ length: count }, (_, r) =>
Array.from({ length: count }, (_, c) => row === r && col === c)
)
);
};
return (
<div className="board">
{cellLights.map((flags, row) => (
<div key={row}>
<Row row={row} flags={flags} toggleCell={toggleCell} />
</div>
))}
</div>
);
}
Row
function Row({
flags,
toggleCell,
row
}: {
row: number;
flags: boolean[];
toggleCell: (row: number, col: number) => void;
}) {
return (
<div className="row">
{flags.map((flag, col) => (
<div key={col}>
<Cell flag={flag} toggleCell={toggleCell.bind(null, row, col)} />
</div>
))}
</div>
);
}
Cell
function Cell({ flag, toggleCell }: { flag: boolean; toggleCell: () => void }) {
return <div className={flag ? "cell high" : "cell"} onClick={toggleCell} />;
}
Demo
Answered By - Drew Reese
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.