我正在构建我的第一个React应用,并遇到这种情况,在这种情况下,我必须在一个方法中两次调用this.setState()。这些调用之一也是在从方法调用的嵌套函数中进行的。
就我而言,我正在开发一个扫雷游戏,而我试图实现的目标是找出玩家在正确标记所有地雷之后是否真正获胜。在包含地雷的每个单元格上设置标志时,玩家会获胜,因此您无法单击它。 为此,我创建了一个名为checkIfWin()的方法,该方法执行以下操作:
此函数从两个不同的地方调用
但是在玩家标记一个单元格之后,我无法找到一种解决方案来声明玩家是否赢得了比赛。这是我的flag()逻辑:
这导致对batch up before executing fully的this.setState()调用,因此当我尝试比较checkIfWin()中的 minesLeft === 0时,存储在state中的值是值状态在上一次标记之前(从1改为0)之前,它没有更新,因为Reac对此this.setState()调用没有堆积逻辑。在JusticeMba article on medium
中也提到了React可以将多个setState()调用批处理为一个更新,以提高性能。
所以我想知道的(抱歉,问题的长度)是,是否有什么办法可以做到这一点?我实际上将此标记方法绑定为Board的render()内部组件的右键单击处理程序。代码如下。我不在乎您是否知道某些代码或伪代码解决方案。我会把两者都考虑进去,因为我似乎没有找到合适的解决方案。
代码:
Inside Board.js render()
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()
}
最后,检查ifWin():
//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()调用来更新的。 >
很抱歉,篇幅太长了,我希望这是可以理解的。
答案 0 :(得分:1)
假设我理解正确,您可以简单地将checkIfWin
作为回调传递到setState
。
rightClickHandler(event, x, y) {
// Rest of your code...
// Pass a callback to your setState
this.setState({
boardData: updatedData,
mineCount: minesLeft,
}, this.checkIfWin);
}
答案 1 :(得分:1)
this.setState()有一个回调,因此您只需提供要运行的内容即可。
this.setState(state, callback);
所以,在那里发生的事情是:
this.setState({
boardData: updatedData,
mineCount: minesLeft,
},
() => {
// Check if the player won or not
});
您也可以替代地编写它:
checkIfWin(){
// Check if the player won or not
}
updateState(){
this.setState(
{
boardData: updatedData,
mineCount: minesLeft,
},
this.checkIfWin());
}
希望这是可以理解的。
答案 2 :(得分:0)
按照Felipe Lanza和Nomi G的方法,通过了解react setState callback的工作原理,我得出了一个可行的解决方案,如下所示:
这是我的代码的样子: 内部rightClickHandler():
rightClickHandler(event, x, y) {
// code...
// Pass a callback to your setState
if(minesLeft === 0) {
//If user flagged possible last tile containing a mine, check if won with a setState callback to checkIfWin()
this.setState(
{
boardData: updatedData,
mineCount: minesLeft,
}
,() => {
this.checkIfWin(this.state.mineCount);
});
} else {
//Rest of logic
}
这就是我调整过的checkIfWin()现在的样子:
checkIfWin() {
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. Update gameStatus
this.setState({
gameStatus: "YOU WON!"
});
}
}
}