简体   繁体   中英

Where to handle parent-child event React

I'm getting start with React basic concepts, and I'm not sure how to proceed...

I'm trying to build a simple Minesweeper game, my structure is a MinesweeperField wrapper, with height, width and minesNumber as props, and a MinesweeperTile, with value (is it a mine/number of close mines) and explored as props.

My problem is, I'm not sure how to handle clicking on the tiles. The MinesweeperField component must be notified when a tile is explored to handle the new game state (ex: in case of game over or win), and it must also be able to trigger explore in other tiles (for example, if you click on a tile with 0 close mines, all of the adjacent tiles must be explored automatically).

From what I understood there are two approaches:

  • Handle click on the Tile, Tile notifies Field, Field triggers explore also on adjacent Tiles - notifying Field from Tile is easy, but calling Tile from Field would require to use refs, plus a different click handler function is instantiated for each Tile
  • Handle click on Field, Field updates the prop that in render is used to assign the isExplored prop to the Tile - since I render the Tile based on an array variable in the Field's state, I have to use setState to trigger a redraw of child Tiles components.

this is a simplified sample of the render function, in the "Handle Click on Field" version:

return (
            <div className="minesweeper-wrapper">
                <div className="minesweeper-field" style={this.getDimensions()}>
                    {this.state.proximityMap.map(function(verticalRow, vIndex){
                        return verticalRow.map(function(tileValue, hIndex){
                            return <MinesweeperTile value={tileValue} hIndex={hIndex} vIndex={vIndex} onClick={this.exploreTile.bind(this, vIndex, hIndex, tileValue)} isExplored={this.state.explorationMap[vIndex][hIndex]} />
                        });    
                    })}
                </div>
            </div>
        );

and here's the exploreTile function:

exploreTile(vIndex, hIndex, tileValue) {
        this.unveilTile(vIndex, hIndex);
        if (tileValue < 0) {
            this.gameOver();
        } else {
            if (tileValue === 0) {
                this.propagateTileExplore(vIndex, hIndex);
            }
        }
    }

this.state.proximityMap contains values indicating how many mines are close to this tile/is this tile a mine. this.state.explorationMap contains bools indicating which tiles have been explored.

My problem with this approach is that, from what I understood, if I want to redraw a single tile, I have to call setState on Field and update this.state.explorationMap array, which will redraw every tile!

Any ideas as to how I could limit the redraw to a single Tile? Should I keep exploring the "handle click on Field" way or go back to "handle click on Tile"?

I started with "handle click on Tile" but I stopped when I got to the "explore adjacent tiles" problem.

Also please no jQuery/js tricks or hacks to solve this in an unconventional way, it's not about finding a way to do this, but finding the most appropriate. It won't probably make a noticeable difference in the context of a Minesweeper game, but as I said, it's for training purposes :)

The most appropriate way would be to let the parent handle the changes. The children just need to know whom to call when they are clicked and how they should look like. I did the same thing in my Game of Life clone using React.

CodePen Link

changeTile(row, col) {
    if (!this.state.isPlaying) {
      const newTile = this.state.board[row][col] === 1 ? 0 : 1;
      const newRow = [...this.state.board[row]];
      newRow.splice(col, 1, newTile);
      const newBoard = [...this.state.board];
      newBoard.splice(row, 1, newRow);
      this.setState({
        board: newBoard
      });
    }
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM