尝试用 React 制作 2048 游戏。状态中的数据在不应该发生变化时发生了变化
Posted
技术标签:
【中文标题】尝试用 React 制作 2048 游戏。状态中的数据在不应该发生变化时发生了变化【英文标题】:Try to make 2048 game in React. Data in the state is mutated when it should not have been 【发布时间】:2018-09-09 19:05:24 【问题描述】:这是我的代码仓库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
【问题讨论】:
【参考方案1】:row.reverse() 将返回反转的数组,但它也会改变行数组。查看 reverse 的 MDN 文档。因此,由于 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的行数组,正因为如此你变异的时候仍然在变异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...
【讨论】:
我传递给flipGrid(copyGrid)
的参数是this.state.gridData
的深拷贝数组,所以它应该不会影响原始数据,对吗?
我使用扩展运算符 [...]
而不是 .slice()
来解决问题
@ZhouBingYang 嘿,我更新了我的答案,如果有什么没有意义的,请再次评论:)
谢谢。是的,我没有意识到在嵌套数组上使用 [...] 正在做浅拷贝。我今天学到了很多。谢谢先生。
@ZhouBingYang 没问题 :)以上是关于尝试用 React 制作 2048 游戏。状态中的数据在不应该发生变化时发生了变化的主要内容,如果未能解决你的问题,请参考以下文章