1

I have a weird bug that I've been trying to debug for a few hours now but just can't seem to figure it out. Essentially my table rows won't render unless a function that calls setState is run.

My table is formatted like so:

<table classname="ui inverted table">
  <thead>
    <tr>
      <th>Lobby name</th>
      <th>Players</th>
      <th>Mode</th>
      <th>Difficulty</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    {this.renderRow()} //Table rows
  </tbody>
</table>

The rows are rendered by this function, that maps over an array of objects:

renderRow = () => {
    return games.map(function(val, i){
         return(
             <tr key={i}>
                 <td>
                     {val.name}
                 </td>
                 <td>
                     {val.currentPlayers}/4
                 </td>
                 <td>
                     {val.gameMode}
                 </td>
                 <td>
                     {val.difficulty}
                 </td>
             </tr>
         )
     })
 }

Now here is the weird bug. The rows won't render unless I tap a button which calls createGame. The only thing createGame does is: this.setState({show: true})

The Menu is:

<div classname="ui inverted segment">
  <div classname="ui large inverted pointing secondary menu">
    <button classname="active item">
      Lobby
    </button>
    <div classname="ui right inverted pointing secondary menu">
      <button classname="item menu_item" onclick={this.createGame}>
        //Have to click this button to render rows for some reason.
        <i classname="plus circle icon"></i>
        Create Game
      </button>
      <div classname="item">
        <img classname="ui mini circular image" src={this.state.photoURL}></img>
        <span classname="menu_name">{this.state.userName}</span>
      </div>
    </div>
  </div>
</div>

CreateGame for reference as well:

createGame = () => {
   this.setState({show: true})
};

It seems like it's really the show attribute in state that's triggering the table rows for some reason, but it's not being conditionally rendered so I don't understand why triggering that state param would cause the rendering. If I manually set show: true in React devtools the table rows render as well.

EDIT: games is being populated like so:

componentDidMount(){
             //DB listner, gets all games on component mount, and all new games. 
             db.collection("games")
             .onSnapshot(function(querySnapshot){
                 querySnapshot.forEach(function(doc){
                     games.push(doc.data())
                 });
                 console.log(games);
             });
}
8
  • we will need some more of the code. for example, how is the renderRow function accessing the games array? is it getting passed in? how is it in scope? Commented Feb 23, 2019 at 3:25
  • I'm thinking it's not rendering because renderRow was not completed during the first render cycle. Commented Feb 23, 2019 at 3:26
  • 1
    how is games being set. maybe it starts off as as an empty array then gets populated some how when you click the button and invoke the createGame function. Commented Feb 23, 2019 at 3:28
  • 1
    Sidenote: Do not use index as element keys. Indexes might change due to inserts, deletes or sorting, resulting in weird rendering issues. Commented Feb 23, 2019 at 3:34
  • 1
    Why not set result as state after retrieving from db and use that state to render rows instead? Commented Feb 23, 2019 at 3:37

1 Answer 1

2

As componentDidMount is only called after the first rendering, your table rows will not be rendered initially. The games array is empty at this point.

It would make sense to move games into the component state here. Thereby automatically updating the state, once the games have been loaded. Remember, a setState will usually trigger a re-render.

componentDidMount(){

  //DB listner, gets all games on component mount, and all new games. 
  db.collection("games").onSnapshot(function(querySnapshot) {

    const loadedGames = querySnapshot.map(function(doc) {
      return doc.data();
    });

    // update the state
    this.setState({ games: loadedGames });

  });

}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the suggestion. This seems to work, but how would I map over this.state.games? Replacing games with this.state.games in my renderRow func doesn't seem to work.
Could be a bind issue. Check that you actually have the right this and access to the state object. Often just adding a couple of console.log statements helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.