我正在构建我的第一个反应应用程序并遇到了这种情况,我必须在方法中两次调用this.setState() . 其中一个调用是在从该方法调用的嵌套函数内部进行的 .
在我的情况下,我试图实现的目的是找出一个玩家在正确标记所有地雷之后是否真的赢了 . 当在包含我的每个单元格上设置标记时,玩家获胜,因此您无法单击它 . 为此,我创建了一个名为checkIfWin()的方法,它执行以下操作:
-
在一个数组中存储板数据信息(每个单元格及其属性),并在计数器中存储剩余的数量 . 两者都是从两个变量 data 和 minesLeft 中的组件状态中提取的
-
如果剩下0个地雷(用户标记的游戏中有多个单元格),则比较两个数组( minesArray 包含棋盘中的每个地雷位置和包含每个标志位置的 flagsArray )
-
如果数组包含相同的信息(标志覆盖每个矿井),你就赢了 . 如果阵列不同,继续播放 .
从两个不同的地方调用此函数
-
玩家点击最后一个不包含地雷的单元格后,每个包含标志的图块都已正确标记
-
玩家标记一个单元格后剩余的地雷覆盖在单板上是0在第一种情况下没有问题,它工作正常 . 我检查 minesLeft 状态是否为=== 0,如果是,我调用方法 checkIfWin() . 由于minesLeft仅在标记单元格后减少,而不是单击一个,因此这里没有问题 .
但是当一个玩家在一个小区上标记后,我无法找到一个解决方案来宣告玩家是否赢得了比赛 . 这是我的flag()逻辑:
-
检查是否覆盖了瓷砖(尚未点击)以及玩家是否尚未获胜(以防止在游戏结束后点击瓷砖)
-
如果图块被覆盖并标记,请取消标记并更新计数器 .
-
如果图块被覆盖,并且未标记,则用标记覆盖并减少 minesLeft 计数器 .
-
当minesLeft为1且Player标记新单元格时,minesLleft现在为0,所以这是我必须检查获胜的时间 .
-
这是现在 where i would like to check if the player did win 要做到这一点,我会打电话给this.setState()新标志的电路板上的位置更新 flagsArray ,过一会儿给checkIfWin() - 即提取存储内国有来自内的值这种方法也是如此 .
这将导致this.setState()调用batch up before executing fully这样,当我尝试比较,如果 minesLeft === 0内部checkIfWin()存储在状态的值,是国家最后萎靡不振之前有(1而不是0)它没有更新,因为Reac 't'堆叠了this.setState()调用的逻辑 . 也在JusticeMba article on medium上提到
React可以将多个setState()调用批处理为单个更新以提高性能 .
所以 WHAT I WANT TO KNOW (对不起问题的长度)是否有任何办法可以做到这一点?我实际上将这个标记方法绑定为Board的render()内部组件上的右键处理程序 . 代码如下 . 我不关心你是否知道代码或伪代码中的某些解决方案 . 我会同时接受它,因为我似乎没有找到适当的解决方案 .
码:
在Board.js里面渲染()
render() {
const board = this.renderBoard(this.state.boardData);
return (
<div className={classes.board}>
<div className={classes.gameInfo}>
<h1>
{this.state.gameStatus}
</h1>
<span className={classes.info}>
Mines remaining: {this.state.mineCount}
</span>
</div>
{board} //This is a Board component that holds Tiles to which the rightClickHandler() is binded as a prop
</div>
用于管理Tiles标记的rightClickHandler():
rightClickHandler(event, x, y) {
event.preventDefault(); // prevents default behaviour such as right click
const clickedTile = { ...this.state.boardData[x][y] }
//ommit if revealed and if game is ended
if (!clickedTile.isRevealed && !(this.state.gameStatus === "YOU WON!")) {
let minesLeft = this.state.mineCount;
//if not revealed it can be flagged or not
if (clickedTile.isFlagged) {
//if flagged, unflag it
clickedTile.isFlagged = false;
minesLeft++;
} else {
//if not flagged, flag it
clickedTile.isFlagged = true;
minesLeft--;
}
//Update the state with new tile and check game status
const updatedData = [...this.state.boardData];
updatedData[x][y] = { ...clickedTile };
this.setState({
boardData: updatedData,
mineCount: minesLeft,
});
//THIS IS WHERE I WOULD LIKE TO checkIfWin()
}
最后,checkIfWin():
//Check game progress and returns a boolean: true if win, false if not.
checkIfWin() {
//No need to create a new array, Ill just reference the one inside state
const data = this.state.boardData;
const minesLeft = this.state.mineCount;
//If flagged as many tiles as mines were initially
if (minesLeft === 0) {
//get mines as an array of positions
const mineArray = this.getMines(data);
//get flags as array of positions
const flagArray = this.getFlags(data);
if (JSON.stringify(mineArray) === JSON.stringify(flagArray)) {
//if both arrays are equal, game is won
return true;
}
} else {
//if more than 0 mines are left return false
return false;
}
}
TL; DR:我试图访问一个组件属性的值,该属性具有过时的状态,该状态被假定为从render()中的JSX组件的单击处理程序内的previos this.setState()调用更新
抱歉长度,我希望这是可以理解的,如果不是我可以编辑清楚 .
3 回答
假设我理解正确,您可以简单地将
checkIfWin
作为回调传递给setState
.this.setState()有一个回调函数,所以你只需要提供运行的内容 .
那么,那里发生的事情是:
你也可以写它:
希望这是可以理解的 .
遵循Felipe Lanza和Nomi G方法并了解react setState callback如何工作,我找到了一个如下工作解决方案:
用户标记了多少个区块,并且板上有地雷(minesLeft === 0),我使用setState回调和checkIfWin()来检查玩家是否赢了
我已经修改了checkIfWin()以在赢得游戏时更新状态,而不是返回布尔值 .
这是我的代码的样子:在rightClickHandler()里面:
这就是我现在调整的checkIfWin():