admin 管理员组文章数量: 1086019
I'm attempting to build a board game similar to Rubik's Race. The board gets randomly generated each time the game loads. This is what I have so far:
interface GameBoardProps {
initialConfiguration: GamePiece[];
}
function GameBoard({ initialConfiguration }: GameBoardProps) {
console.log("Drawing board");
const [gamePieces, setGamePieces] = useState(initialConfiguration);
for (let gamePiece of gamePieces) {
console.log(gamePiece.color);
}
const gamePieceSize = gamePieces[0].size;
const isEmptySpace = (index: number): boolean => {
return gamePieces[index].color === "black";
};
const isAtLeftEdge = (index: number): boolean => {
return index % 5 === 0;
};
const isAtRightEdge = (index: number): boolean => {
return index % 5 === 4;
};
const isAtTopEdge = (index: number): boolean => {
return index < 5;
};
const isAtBottomEdge = (index: number): boolean => {
return index >= 20;
};
const shiftLeft = (leftIndex: number, index: number) => {
setGamePieces((gamePieces) => {
const gamePiecesCopy = [...gamePieces];
var tempNullPiece = gamePiecesCopy[leftIndex];
for (; leftIndex < index; leftIndex++)
gamePiecesCopy[leftIndex] = gamePiecesCopy[leftIndex + 1];
gamePiecesCopy[leftIndex] = tempNullPiece;
return gamePiecesCopy;
});
};
const shiftRight = (rightIndex: number, index: number) => {
setGamePieces((gamePieces) => {
const gamePiecesCopy = [...gamePieces];
var tempNullPiece = gamePiecesCopy[rightIndex];
for (; rightIndex > index; rightIndex--)
gamePiecesCopy[rightIndex] = gamePiecesCopy[rightIndex - 1];
gamePiecesCopy[rightIndex] = tempNullPiece;
return gamePiecesCopy;
});
};
const shiftUp = (topIndex: number, index: number) => {
setGamePieces((gamePieces) => {
const gamePiecesCopy = [...gamePieces];
var tempNullPiece = gamePiecesCopy[topIndex];
for (; topIndex < index; topIndex += 5)
gamePiecesCopy[topIndex] = gamePiecesCopy[topIndex + 5];
gamePiecesCopy[topIndex] = tempNullPiece;
return gamePiecesCopy;
});
};
const shiftDown = (bottomIndex: number, index: number) => {
setGamePieces((gamePieces) => {
const gamePiecesCopy = [...gamePieces];
var tempNullPiece = gamePiecesCopy[bottomIndex];
for (; bottomIndex > index; bottomIndex -= 5)
gamePiecesCopy[bottomIndex] = gamePiecesCopy[bottomIndex - 5];
gamePiecesCopy[bottomIndex] = tempNullPiece;
return gamePiecesCopy;
});
};
const handleClick = (index: number) => {
let left = index,
right = index,
top = index,
bottom = index;
let shifted = false;
console.log("Clicked");
while (!(shifted || isAtLeftEdge(left))) {
console.log("Checking left side");
left -= 1;
if (isEmptySpace(left)) {
shiftLeft(left, index);
shifted = true;
}
}
while (!(shifted || isAtRightEdge(right))) {
console.log("Checking right side");
right += 1;
if (isEmptySpace(right)) {
shiftRight(right, index);
shifted = true;
}
}
while (!(shifted || isAtTopEdge(top))) {
console.log("Checking top side");
top -= 5;
if (isEmptySpace(top)) {
shiftUp(top, index);
shifted = true;
}
}
while (!(shifted || isAtBottomEdge(bottom))) {
console.log("Checking bottom side");
bottom += 5;
if (isEmptySpace(bottom)) {
shiftDown(bottom, index);
shifted = true;
}
}
};
return (
<div
className="game-board"
style={{
gridTemplate: `repeat(5, ${gamePieceSize}) / repeat(5, ${gamePieceSize})`,
}}
>
{gamePieces.map((piece, index) => (
<Square
key={piece.id}
size={piece.size}
color={piece.color}
onClick={() => handleClick(index)}
/>
))}
</div>
);
}
GameBoard is initialized from the App component like so:
const randomizePieces = (gamePieceSize: string) => {
console.log("randomizing board");
const gameBoard: GamePiece[] = [
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "blue" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "white" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "green" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "orange" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "red" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "yellow" })),
{ id: 0, size: gamePieceSize, color: "black" },
];
for (let i = 0; i < 15; i++) {
const randA = Math.floor(Math.random() * gameBoard.length),
randB = Math.floor(Math.random() * gameBoard.length);
const temp = gameBoard[randA];
gameBoard[randA] = gameBoard[randB];
gameBoard[randB] = temp;
}
for (let i = 0; i < gameBoard.length; i++) gameBoard[i].id = i;
return gameBoard;
};
const gamePieces = randomizePieces("50px");
function App() {
return <GameBoard initialConfiguration={gamePieces} />;
}
The unexpected behavior I'm noticing is this: Initially after loading the game board, game pieces are able to shift around the first time a valid board piece is clicked, but they do not shift around on consecutive clicks. I notice on consecutive clicks that the logs are being printed, indicating that the appropriate functions are being called, but nothing is getting redrawn. This is only happening in StrictMode though; when I disable StrictMode the board behaves as expected (shifting valid pieces into the "empty" space). Anyone know what's happening and how I can change the code to work in development, using StrictMode?
I'm attempting to build a board game similar to Rubik's Race. The board gets randomly generated each time the game loads. This is what I have so far:
interface GameBoardProps {
initialConfiguration: GamePiece[];
}
function GameBoard({ initialConfiguration }: GameBoardProps) {
console.log("Drawing board");
const [gamePieces, setGamePieces] = useState(initialConfiguration);
for (let gamePiece of gamePieces) {
console.log(gamePiece.color);
}
const gamePieceSize = gamePieces[0].size;
const isEmptySpace = (index: number): boolean => {
return gamePieces[index].color === "black";
};
const isAtLeftEdge = (index: number): boolean => {
return index % 5 === 0;
};
const isAtRightEdge = (index: number): boolean => {
return index % 5 === 4;
};
const isAtTopEdge = (index: number): boolean => {
return index < 5;
};
const isAtBottomEdge = (index: number): boolean => {
return index >= 20;
};
const shiftLeft = (leftIndex: number, index: number) => {
setGamePieces((gamePieces) => {
const gamePiecesCopy = [...gamePieces];
var tempNullPiece = gamePiecesCopy[leftIndex];
for (; leftIndex < index; leftIndex++)
gamePiecesCopy[leftIndex] = gamePiecesCopy[leftIndex + 1];
gamePiecesCopy[leftIndex] = tempNullPiece;
return gamePiecesCopy;
});
};
const shiftRight = (rightIndex: number, index: number) => {
setGamePieces((gamePieces) => {
const gamePiecesCopy = [...gamePieces];
var tempNullPiece = gamePiecesCopy[rightIndex];
for (; rightIndex > index; rightIndex--)
gamePiecesCopy[rightIndex] = gamePiecesCopy[rightIndex - 1];
gamePiecesCopy[rightIndex] = tempNullPiece;
return gamePiecesCopy;
});
};
const shiftUp = (topIndex: number, index: number) => {
setGamePieces((gamePieces) => {
const gamePiecesCopy = [...gamePieces];
var tempNullPiece = gamePiecesCopy[topIndex];
for (; topIndex < index; topIndex += 5)
gamePiecesCopy[topIndex] = gamePiecesCopy[topIndex + 5];
gamePiecesCopy[topIndex] = tempNullPiece;
return gamePiecesCopy;
});
};
const shiftDown = (bottomIndex: number, index: number) => {
setGamePieces((gamePieces) => {
const gamePiecesCopy = [...gamePieces];
var tempNullPiece = gamePiecesCopy[bottomIndex];
for (; bottomIndex > index; bottomIndex -= 5)
gamePiecesCopy[bottomIndex] = gamePiecesCopy[bottomIndex - 5];
gamePiecesCopy[bottomIndex] = tempNullPiece;
return gamePiecesCopy;
});
};
const handleClick = (index: number) => {
let left = index,
right = index,
top = index,
bottom = index;
let shifted = false;
console.log("Clicked");
while (!(shifted || isAtLeftEdge(left))) {
console.log("Checking left side");
left -= 1;
if (isEmptySpace(left)) {
shiftLeft(left, index);
shifted = true;
}
}
while (!(shifted || isAtRightEdge(right))) {
console.log("Checking right side");
right += 1;
if (isEmptySpace(right)) {
shiftRight(right, index);
shifted = true;
}
}
while (!(shifted || isAtTopEdge(top))) {
console.log("Checking top side");
top -= 5;
if (isEmptySpace(top)) {
shiftUp(top, index);
shifted = true;
}
}
while (!(shifted || isAtBottomEdge(bottom))) {
console.log("Checking bottom side");
bottom += 5;
if (isEmptySpace(bottom)) {
shiftDown(bottom, index);
shifted = true;
}
}
};
return (
<div
className="game-board"
style={{
gridTemplate: `repeat(5, ${gamePieceSize}) / repeat(5, ${gamePieceSize})`,
}}
>
{gamePieces.map((piece, index) => (
<Square
key={piece.id}
size={piece.size}
color={piece.color}
onClick={() => handleClick(index)}
/>
))}
</div>
);
}
GameBoard is initialized from the App component like so:
const randomizePieces = (gamePieceSize: string) => {
console.log("randomizing board");
const gameBoard: GamePiece[] = [
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "blue" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "white" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "green" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "orange" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "red" })),
...Array(4)
.fill(null)
.map(() => ({ id: 0, size: gamePieceSize, color: "yellow" })),
{ id: 0, size: gamePieceSize, color: "black" },
];
for (let i = 0; i < 15; i++) {
const randA = Math.floor(Math.random() * gameBoard.length),
randB = Math.floor(Math.random() * gameBoard.length);
const temp = gameBoard[randA];
gameBoard[randA] = gameBoard[randB];
gameBoard[randB] = temp;
}
for (let i = 0; i < gameBoard.length; i++) gameBoard[i].id = i;
return gameBoard;
};
const gamePieces = randomizePieces("50px");
function App() {
return <GameBoard initialConfiguration={gamePieces} />;
}
The unexpected behavior I'm noticing is this: Initially after loading the game board, game pieces are able to shift around the first time a valid board piece is clicked, but they do not shift around on consecutive clicks. I notice on consecutive clicks that the logs are being printed, indicating that the appropriate functions are being called, but nothing is getting redrawn. This is only happening in StrictMode though; when I disable StrictMode the board behaves as expected (shifting valid pieces into the "empty" space). Anyone know what's happening and how I can change the code to work in development, using StrictMode?
Share Improve this question asked Mar 27 at 18:31 Oni BarolliOni Barolli 931 gold badge1 silver badge9 bronze badges 1- Step 1: ignore React, do all of it in plain JS and then only as almost an after thought, turn the resulting JS data structure into a bunch of JSX elements. Because this is one of those things where it's completely irrelevant what framework you're using, you first generate your data, then in as little code as possible you map that data to UI elements. Anything done "inside" a React component that isn't about recording state updates and turning finalized data into UI elements is code that doesn't belong there. – Mike 'Pomax' Kamermans Commented Mar 27 at 18:48
1 Answer
Reset to default 0StrictMode calls your state setters twice (but only keeps the result of the second call). Because of this your setters have to be idempotent, but yours have a tricky side-effect!
In your shift handlers, e.g., shiftUp
, your setter, setGamePieces(...)
, is capturing the arguments passed, topIndex
and index
, then inside the setter, you are modifying topIndex
in the for loop. So here's what happens:
- StrictMode calls setGamePieces() the first time with
topIndex
= 12 andindex
= 22 (for example) - In the setter, your for loop updates
topIndex
to 22 while it shifts the pieces correctly - Your setter returns the correct updated state
- StrictMode discards this result, and calls the setter a second time
- This time,
topIndex
is already 22, so it doesn't make any changes because it is already >=index
, and your setter returns the state effectively unchanged. - StrictMode accepts this result and re-renders
To fix it, use a new indexing variable instead of modifying the topIndex
directly, e.g.,
var i = topIndex;
var tempNullPiece = gamePiecesCopy[i];
for (; i < index; i += 5)
gamePiecesCopy[topIndex] = gamePiecesCopy[i + 5];
gamePiecesCopy[i] = tempNullPiece;
本文标签: reactjsHow to create a randomly generated grid of colored squares in ReactStack Overflow
版权声明:本文标题:reactjs - How to create a randomly generated grid of colored squares in React - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://roclinux.cn/p/1744073279a2528882.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论