[英]How do I access a component's state in a function that is inside another function that calls this.setState()?
I'm building my first react app and came across this situation in which I have to call this.setState() twice within a method.我正在构建我的第一个 react 应用程序并遇到了这种情况,在这种情况下,我必须在一个方法中调用 this.setState() 两次。 One of those calls is made inside a nested function called from the method as well.
其中一个调用也是在从该方法调用的嵌套函数中进行的。
In my case, I'm building a minesweeper game, and what I'm trying to achieve is to find out if a player has actually won after flagging all mines correctly.就我而言,我正在构建一个扫雷游戏,我想要实现的是在正确标记所有地雷后找出玩家是否真的赢了。 A player wins when setting flags over every cell that contains a mine so you cannot click on it.
在每个包含地雷的单元格上设置标志以便您无法点击它时,玩家获胜。 To do so I've created a method called checkIfWin() that does the following:
为此,我创建了一个名为 checkIfWin() 的方法,它执行以下操作:
This function is called from two different places这个函数是从两个不同的地方调用的
But after a player flags on a cell, I cannot manage to find a solution to declare if a player has won the game or not.但是在玩家在单元格上标记后,我无法设法找到一个解决方案来声明玩家是否赢得了比赛。 This is my flag() logic:
这是我的 flag() 逻辑:
This results in the this.setState() call to batch up before executing fully so that when I try to compare if minesLeft ===0 inside checkIfWin() the value stored in state is the value state had before the last flagging (1 instead of 0), it didn't update because of React's stacking up logic for this.setState() calls.这导致 this.setState() 调用在完全执行之前进行批处理,以便当我尝试比较 checkIfWin() 中是否minesLeft ===0 时,存储在 state 中的值是最后一次标记之前的值 state(改为 1 0),它没有更新,因为 React 的 this.setState() 调用的堆叠逻辑。 Also mentioned on JusticeMba article on medium
媒体上的JusticeMba文章也提到了
React may batch multiple setState() calls into a single update for performance.
React 可以将多个 setState() 调用批处理为单个更新以提高性能。
So WHAT I WANT TO KNOW (sorry for the length of the question) is if there is any way that I could do this?所以我想知道的是(对不起,问题的长度)是否有任何方法可以做到这一点? I'm actually binding this flagging method as a right click handler on a component inside render() of the Board.
我实际上将此标记方法绑定为 Board 的 render() 内组件上的右键单击处理程序。 Code will follow below.
代码将在下面进行。 I don't care about if you know some solution in code or pseudo code.
我不在乎你是否知道代码或伪代码中的一些解决方案。 I would take both it in, since I don't seem to find a proper fix for this.
我会同时接受它,因为我似乎没有找到合适的解决方法。
Code:代码:
Inside Board.js 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>
The rightClickHandler() that manages flagging of Tiles:管理 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()
}
Lastly, checkIfWin():最后,checkIfWin():
//Check game progress and returns a boolean: true if win, false if not.
checkIfWin() {
//No need to create a new array, I'll 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: I'm trying to access the value of a component's property with an outdated state that was supposed to be updated from a previous this.setState() call inside a click handler of a JSX component inside render() TL; DR:我正在尝试访问具有过时状态的组件属性的值,该状态应该从之前的 this.setState() 调用在 render() 内的 JSX 组件的单击处理程序中更新
Sorry for the length, and I hope it is understandable, if not I can edit for clarity.对不起,长度,我希望它是可以理解的,如果不是,我可以编辑清楚。
Assuming I understood it right, you could simply pass checkIfWin
as a callback to your setState
.假设我理解正确,您可以简单地将
checkIfWin
作为回调传递给您的setState
。
rightClickHandler(event, x, y) {
// Rest of your code...
// Pass a callback to your setState
this.setState({
boardData: updatedData,
mineCount: minesLeft,
}, this.checkIfWin);
}
this.setState() has a callback, so up there you are just providing the what to run. this.setState() 有一个回调,所以在那里你只是提供要运行的内容。
this.setState(state, callback);
So, up there what's happening there is:所以,那里正在发生的事情是:
this.setState({
boardData: updatedData,
mineCount: minesLeft,
},
() => {
// Check if the player won or not
});
Also you can write it alternatively:你也可以写成:
checkIfWin(){
// Check if the player won or not
}
updateState(){
this.setState(
{
boardData: updatedData,
mineCount: minesLeft,
},
this.checkIfWin());
}
Hope it was understandable.希望这是可以理解的。
Following Felipe Lanza and Nomi G approach and by understanding how react setState callback works, I've come to a working solution that is as follows:遵循Felipe Lanza和Nomi G 的方法并通过了解react setState 回调的工作原理,我得出了一个可行的解决方案,如下所示:
This is what my code looks like: Inside rightClickHandler():这是我的代码的样子:在 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
}
And this is what my tweaked checkIfWin() looks like now:这就是我调整后的 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!"
});
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.