0

这是我的代码仓库https://github.com/540376482yzb/2048game_react

这是我的现场演示https://objective-fermat-0343a3.netlify.com/

游戏的当前状态只向左和向右。

但是,当没有移动时,它会在按下左箭头键时生成新数字。已识别的问题this.state.gridData在我运行之前被翻转,this.diffGrid(grid)因此它将始终返回 true 并添加新数字。

这个问题的嫌疑人是:

    // issue ====>  flipGrid function is mutating this.state.gridData
flipGrid(grid) {
    return grid.map(row => row.reverse())
}

或者

    //slide left
            if (e.keyCode === 37) {
                //issue ===> state is flipped on left arrow key pressed
                copyGrid = this.flipGrid(copyGrid).map(row => this.slideAndCombine(row))
                copyGrid = this.flipGrid(copyGrid)
            }

有人能告诉我我在哪里做错导致状态突变吗?

import React from 'react'
import './App.css'
import Grid from './Grid'
class App extends React.Component {
	constructor(props) {
		super(props)
		this.state = {
			gridData: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
		}
	}
	initGame() {
		let grid = [...this.state.gridData]
		grid = this.addNumber(grid)
		grid = this.addNumber(grid)
		this.setState({
			gridData: grid
		})
	}

	addNumber(grid) {
		const availableSpot = []
		grid.map((rowData, x) =>
			rowData.map((data, y) => {
				if (!data) availableSpot.push({ x, y })
			})
		)
		const randomSpot = availableSpot[Math.floor(Math.random() * availableSpot.length)]
		grid[randomSpot.x][randomSpot.y] = Math.random() < 0.2 ? 4 : 2
		return grid
	}
	slide(row) {
		const newRow = row.filter(data => data)
		const zerosArr = Array(4 - newRow.length).fill(0)
		return [...zerosArr, ...newRow]
	}

	combine(row) {
		let a, b
		for (let i = 3; i > 0; i--) {
			a = row[i]
			b = row[i - 1]
			if (a === b) {
				row[i] = a + b
				row[i - 1] = 0
			}
		}
		return row
	}

	slideAndCombine(row) {
		row = this.slide(row)
		row = this.combine(row)
		return row
	}

	diffGrid(grid) {
		let isDiff = false
		for (let i = 0; i < grid.length; i++) {
			for (let j = 0; j < grid.length; j++) {
				if (grid[i][j] != this.state.gridData[i][j]) {
					isDiff = true
				}
			}
		}
		return isDiff
	}

	// issue ====>  flipGrid function is mutating this.state.gridData
	flipGrid(grid) {
		return grid.map(row => row.reverse())
	}

	componentDidMount() {
		this.initGame()
		let copyGrid = [...this.state.gridData]
		window.addEventListener('keyup', e => {
			if (e.keyCode === 37 || 38 || 39 || 40) {
				//slide right
				if (e.keyCode === 39) {
					copyGrid = copyGrid.map(row => this.slideAndCombine(row))
				}
				//slide left
				if (e.keyCode === 37) {
					//issue ===> state is flipped on left arrow key pressed
					copyGrid = this.flipGrid(copyGrid).map(row => this.slideAndCombine(row))
					copyGrid = this.flipGrid(copyGrid)
				}

				// Line 89 issue==>>>>>> gridData in the state
				console.table(this.state.gridData)

				// diffGrid compares copyGrid with this.state.gridData
				if (this.diffGrid(copyGrid)) {
					copyGrid = this.addNumber(copyGrid)
					//deepCopy of gridData
					console.table(copyGrid)

					this.setState({
						gridData: copyGrid
					})
				}
			}
		})
	}

	render() {
		// Line 103 ===>>>> gridData in the state
		console.table(this.state.gridData)
		return (
			<div className="App">
				<main className="centerGrid" id="game">
					<Grid gridData={this.state.gridData} />
				</main>
			</div>
		)
	}
}

export default App

4

1 回答 1

2

row.reverse() 将返回反转的数组,但它也会改变行数组。查看 MDN 文档以了解reverse。因此,因为 map 在迭代元素之前不会复制元素,所以您的 flipGrid 函数将改变传递给它的网格。

为确保您不会改变原始网格,您可以执行以下操作:

flipGrid(grid) {
    const gridCopy = grid.slice()
    return gridCopy.map(row => row.reverse())
}

或者更简洁一点:

flipGrid(grid) {
    return grid.slice().map(row => row.reverse())
}

或者如果您想使用扩展运算符:

flipGrid(grid) {
    return [...grid].map(row => row.reverse())
}

至于为什么this.state.gridData会发生变异:数组扩展实际上是一个浅拷贝,所以gridCopy仍然引用this.state.gridData的行数组,因此你在使用flipGrid变异时仍然在变异this.state.gridData。如果您完全停止变异,这无关紧要,但最好在事件处理程序的开头而不是在 componentDidMount 中进行深度复制,这样一开始就不会发生这种情况。

您可以使用 lodash 的 cloneDeep 函数或在 gridCopy 上映射并浅复制它们(使用扩展运算符或切片)

window.addEventListener('keyup', e => {
    if (e.keyCode === 37 || 38 || 39 || 40) {
        let gridCopy = this.state.gridData.map((row) => row.slice())
        etc...
    }
}
于 2018-03-30T20:03:18.127 回答