0

I'm creating a minesweeper React app and inside the Board stateful component I've initialized and populated the board with mines and other information.

The initialization of this board is made as follows

// Method that creates a board of empty tiles, populates it with mines and gets the number of neighbouring
// mines for every tile on the board. Returns the initialized board  
initializeBoard(height, width, mines){
    const emptyTiles = this.createEmptyArray(height, width);
    const populatedTiles = this.populateBoard(emptyTiles, height, width, mines);
    const neighbourTiles = this.getNumOfMinesNeighbours(populatedTiles,height, width);
    return this.renderBoard(neighbourTiles);
   }

renderBoard returns a JSX component.

I receive height, width and mines as props passed from another component.

My Board status looks like this:

state = {
    mineCount: this.props.mines,
    gameStatus: null,
    boardData: null
}

And I want to dynamically render the initialized board and update Board's state value for boardData key. Now, I've arrived two different approaches for this:

  1. dynamically call initialize board() in Board's render() method, and update its state somehow
  2. Directly assign the state boardData value with a call to initializeBoard()

Approach #1 would look like this, inside render():

render() {  
    const board= this.initializeBoard(this.props.height, this.props.width, this.props.mines);
    //Save state throws Maximum update depth exceeded.
    return (
        <div>
            {board}
        </div> ...} //end of render

Approach #2:

state = {
    mineCount: this.props.mines,
    gameStatus: null,
    boardData: this.initializeBoard(this.props.height, this.props.width, this.props.mineCount)
}

Now, I know that setting the state of the component inside render() is a no-no, but also I am unable to find a proper lifecycle hook that will work when an object is created prior to render() and then dynamically rendered into JSX, since I'm not receiving this object via props.

So What I want to know is this:

  • Is the approach #2 a good/appropriate one? Or is calling a method to store a state's key value a bad practice?
  • Is there something I'm missing when it comes to lifecycle hooks and objects created prior to render()?
  • Is there any 'best practice' to store a state's key value in this situation?
Juan Giacosa
  • 183
  • 1
  • 12
  • Approach 1 should be fine? You're not calling `setState` within your `initBoard` right? – stackoverfloweth Aug 28 '18 at 20:05
  • and then `renderBoard` just returns a jsx element right? – stackoverfloweth Aug 28 '18 at 20:06
  • 1- No, i'm not calling setState on initBoard but I cannot do it in any place, because at this point state's boardData is still null And I have to update it with the returned board from renderBoard. 2- renderBoard returns a JSX element indeed, I'll edit that on the question. – Juan Giacosa Aug 28 '18 at 20:14

3 Answers3

0

Is the approach #2 a good/appropriate one? Or is calling a method to store a state's key value a bad practice?

No, why should it?

Is there something I'm missing when it comes to lifecycle hooks and objects created prior to render()?

No. there is no valid reason to hook in there.

Is there any 'best practice' to store a state's key value in this situation?

yes, #2.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
0

Maybe more comments in the code will help me to better understand the problem.

constructor(super) {
   state = {
     mineCount: this.props.mines,
     gameStatus: null,
     boardData: null
   }
   this.board = null
   this.populatedTiles = null;
   this.neighbourTiles = null; 
   this.width = this.props.width;
   this.height = this.props.height; 
}

componentDidMount() {
   this.initializeBoard(this.height, this.width, this.state.mines);
}

initializeBoard(height, width, mines){
   const emptyTiles = this.createEmptyArray(height, width);
   this.populatedTiles = this.populateBoard(emptyTiles, height, width, mines);
   this.neighbourTiles = this.getNumOfMinesNeighbours(populatedTiles,height, width);
}

render() {
   const board = this.renderBoard(this.neighbourTiles, this.state) || null;
   return (
     <div>
        {board}
     </div>
   )
} 
joseluismurillorios
  • 1,005
  • 6
  • 11
  • Sure! Let me know that you would like to understand better so I can expand. Would you please let me know a little bit more about why you did what you did there? – Juan Giacosa Aug 28 '18 at 23:50
  • I don't let props that aren't going to change in the state, but about your questions i think i need to know what those functions really do. – joseluismurillorios Aug 29 '18 at 06:05
0

The previous poster answered your first two questions, I'll try to expand a little on the third.

The short answer is #2 is the better practice.

Try to think of this more as an Object Oriented Programming problem than a react problem. The react component you're referencing is just an instance of a react component class. Like any class, they have a constructor function. The idea is that you want to do any state initialization in the constructor. You want to have your "source of truth" ready to go when you first instantiate the class.

Therefore, it's a good idea to do your board initialization in the constructor when you first set state. This way, you avoid any inconsistencies in state between when you first instantiate the class and attempt to manipulate the board. It's also cleaner, easier to understand, and in line with typical OOP principles.