首页 > 解决方案 > 如何在另一个调用 this.setState() 的函数内部的函数中访问组件的状态?

问题描述

我正在构建我的第一个反应应用程序,遇到了这种情况,我必须在一个方法中调用 this.setState() 两次。其中一个调用也是在从该方法调用的嵌套函数中进行的。

就我而言,我正在构建一个扫雷游戏,我想要实现的是找出玩家在正确标记所有地雷后是否真的赢了。玩家在每个包含地雷的单元格上设置标志时获胜,因此您无法点击它。为此,我创建了一个名为 checkIfWin() 的方法,它执行以下操作:

  1. 将棋盘数据信息(每个单元及其属性)存储在一个数组中,并将剩余地雷的数量存储在一个计数器中。两者都是从组件状态中提取的两个变量,称为dataminesLeft
  2. 如果剩余 0 个地雷(用户标记的单元格与游戏中的地雷数一样多),则比较两个数组(minesArray包含棋盘中的每个地雷位置,flagsArray包含每个标志位置)
  3. 如果数组包含相同的信息(标志覆盖了每个地雷),你就赢了。如果数组不同,请继续播放。

这个函数是从两个不同的地方调用的

  1. 在玩家单击最后一个不包含地雷的单元格并且每个包含标志的图块都已正确标记后
  2. 在玩家标记一个单元并且棋盘中要覆盖的剩余地雷为 0 后,在第一种情况下没有问题,它可以正常工作。我检查minesLeft的状态是否为 === 0,如果是,我调用方法checkIfWin()。由于 minesLeft 仅在标记一个单元格后减少,而不是单击一个单元格,所以这里没有问题。

但是在玩家在单元格上标记后,我无法找到解决方案来宣布玩家是否赢得了比赛。这是我的 flag() 逻辑:

  1. 检查瓷砖是否被覆盖(尚未被点击)以及玩家是否尚未获胜(以防止在游戏结束后点击瓷砖)
  2. 如果瓷砖被覆盖并标记,则取消标记并更新计数器。
  3. 如果瓷砖被覆盖,并且没有被标记,那么用一个标记覆盖它并减少minesLeft计数器。
  4. minesLeft为 1 并且 Player 标记一个新单元格时, minesLleft 现在为 0,所以这是我必须检查是否获胜的时候。
  5. 现在这是我想检查玩家是否获胜的地方为此,我必须调用 this.setState() 以使用棋盘上新标志的位置更新flagsArray ,然后调用 checkIfWin() -that也从该方法中提取存储在 state- 中的值。

这导致 this.setState() 调用在完全执行之前进行批处理,因此当我尝试比较checkIfWin() 中的minesLeft ===0 时,存储在 state 中的值是最后一次标记之前 state 的值(1 而不是0),由于 React 的 this.setState() 调用的堆叠逻辑,它没有更新。也提到了关于介质的JusticeMba文章

React 可以将多个 setState() 调用批处理到单个更新中以提高性能。

那么我想知道的是(抱歉问题的长度)是否有任何方法可以做到这一点?我实际上将此标记方法绑定为 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() 

    }

最后,检查IfWin():

//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:我正在尝试访问具有过时状态的组件属性的值,该状态应该从先前的 this.setState() 调用中更新,该调用在 render() 中的 JSX 组件的单击处理程序内

对不起,我希望它是可以理解的,如果不是,我可以编辑清楚。

标签: javascriptreactjsreact-nativelogicpseudocode

解决方案


假设我理解正确,您可以简单地将checkIfWin回调传递给您的setState.

rightClickHandler(event, x, y) {
    // Rest of your code...

    // Pass a callback to your setState
    this.setState({
        boardData: updatedData,
        mineCount: minesLeft,
    }, this.checkIfWin);

}

推荐阅读