【发布时间】:2020-01-15 21:28:48
【问题描述】:
我正在尝试在 React 中实现扫雷,每当玩家单击地雷时,板都会重置并重新渲染,但玩家最初单击的包含地雷的单元格似乎会在板重置后再次触发 onClick . 我还注意到,如果我在击中地雷后不重置棋盘,而是调用 alert() 然后在不改变状态的情况下返回,那么游戏会循环直到发生堆栈溢出。
当我在游戏结束后显示警报并且不更改状态时,我的有状态棋盘组件的外观如下:
render() {
let squareGrid = this.state.currentGrid.slice();
return (
squareGrid.map((row, y) => { //For each row
return ( //Create a division
<div key={y}>
{
row.map((state, x) => {//Render a square for each index
let value = (state.touched) ? state.minedNeighbors :"_";
return <Square mine={squareGrid[y][x].mine} key={x} disabled={state.touched} val={value}
onClick={() => this.handleClick(y, x)}> </Square>
})}
</div>
)
}
)
)
}
handleClick(row, column) {
// Get copy of grid
const grid = this.state.currentGrid.slice();
//If the player clicks a mine, game over.
if (grid[row][column].mine) {
//this.resetGame(); //This function does cause a state change
alert("You have died.");
return;
}
//Non-pure function that mutates grid
this.revealNeighbors(row, column, grid);
this.setState({
currentGrid: grid
})
}
我的 Square 组件是一个函数
function Square(props) {
return (
<button className={"gameButton"} disabled={props.disabled} onClick={props.onClick}>
{props.val}
</button>
);
}
一旦玩家点击了地雷,代码就会一遍又一遍地反复显示警报。 如果我在 handleClick 中取消注释重置游戏的行,板将正确重置,但玩家最后点击的单元格将显示为玩家在板重置后再次点击它。
许多其他帖子遇到我的问题是由于 onClick 属性包含函数调用而不是函数指针,但据我所知,我不是直接在渲染中调用函数;我提供了一个闭包。
编辑: 这是我的 Board 组件的完整代码。
class Board extends React.Component {
constructor(props) {
super(props);
let grid = this.createGrid(_size);
this.state = {
size: _size,
currentGrid: grid,
reset: false
}
}
createGrid(size) {
const grid = Array(size).fill(null);
//Fill grid with cell objects
for (let row = 0; row < size; row++) {
grid[row] = Array(size).fill(null);
for (let column = 0; column < size; column++) {
grid[row][column] = {touched: false, mine: Math.random() < 0.2}
}
}
//Reiterate to determine how many mineNeighbors each cell has
for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) {
grid[r][c].minedNeighbors = this.countMineNeighbors(r, c, grid)
}
}
return grid;
}
handleClick(row, column) {
const grid = this.state.currentGrid.slice();
//If the player clicks a mine, game over.
if (grid[row][column].mine) {
//this.resetGame();
//grid[row][column].touched = true;
alert("You have died.");
return;
}
//Non-pure function that mutates grid
this.revealNeighbors(row, column, grid);
this.setState({
currentGrid: grid
})
}
//Ensure cell is in bounds
checkBoundary(row, column) {
return ([row, column].every(x => 0 <= x && x < this.state.size));
}
revealNeighbors(row, column, grid) {
//Return if out of bounds or already touched
if (!this.checkBoundary(row, column) || grid[row][column].touched) {
return;
}
//Touch cell
grid[row][column].touched = true;
if (grid[row][column].minedNeighbors === 0) {
//For each possible neighbor, recurse.
[[1, 0], [-1, 0], [0, 1], [0, -1]]
.forEach(pos => this.revealNeighbors(row + pos[0], column + pos[1], grid));
}
}
countMineNeighbors(row, column, grid) {
let size = grid.length;
//Returns a coordinate pair representing the position of the cell in the direction of the angle, eg, Pi/4 radians -> [1,1]
let angleToCell = (angle) => [Math.sin, Math.cos]
.map(func => Math.round(func(angle)))
.map((val, ind) => val + [row, column][ind]);
return Array(8)
.fill(0)
.map((_, ind) => ind * Math.PI / 4) //Populate array with angles toward each neighbor
.map(angleToCell)
.filter(pos => pos.every(x => 0 <= x && x < size))//Remove out of bounds cells
.filter(pos => grid[pos[0]][pos[1]].mine)//Remove cells that aren't mines
.length //Return the length of the array as the count
}
resetGame() {
this.setState({
currentGrid: this.createGrid(this.state.size)
}
)
}
render() {
let squareGrid = this.state.currentGrid.slice();
return (
squareGrid.map((row, y) => { //For each rows
return ( //Create a division
<div key={y}>
{
row.map((state, x) => {//Render a square for each index
let value = (state.touched) ? state.minedNeighbors : "_";
return <Square mine={squareGrid[y][x].mine} key={x} disabled={state.touched} val={value}
onClick={() => this.handleClick(y, x)}/>
})}
</div>
)
}
)
)
}
}
【问题讨论】:
-
控制台输出中是否有任何警告或错误?
-
这个
this.revealNeighbors发生了什么? -
您能否提供一个交互式示例(即 jsfiddle)或至少提供完整的组件代码?
-
除了无限循环之外,我没有看到任何错误。
-
@BrianThompson 递归搜索提供给它的本地网格,并触摸所有没有我的邻居的单元格。它不会更改全局状态,而是返回修改后的本地网格。我已经提供了它的代码。
标签: javascript reactjs