Minh Nguyen
Minh Nguyen

Reputation: 39

How to make the component "wait" for state to update in react?

I'm learning react by making a battleship game. When the component loads, I create a new object (board) which I set as a state. Then I'd like to map the board itself, which is any array. However, react says cannot read property board of undefined. With console logging, I found out that at first when the page loads, playerBoard is an empty object, and only THEN sets it to the given object with setPlayerBoard. How could I avoid this?

App.js looks like this:

const GameControl = () => {
    const [playerBoard, setPlayerBoard] = useState({})
    
    //creating the board object when component mounts, setting it as a state
    useEffect(() => {
        const playerBoard = GameBoard('player');
        setPlayerBoard({...playerBoard});
    },[])

    return (
        <div className = 'board-container'>
            <div className = "board player-board">

            {   //mapping an array
                playerBoard.boardInfo.board.map((cell, i) => {
                    return (
                        <div className = {`cell player-cell`key = {i}></div>
                    )
                } )
            }
            </div>
       </div>
    )

}

Upvotes: 2

Views: 1929

Answers (1)

Nicholas Tower
Nicholas Tower

Reputation: 84902

If creating the game board is synchronous, then just use that as your default value for the state:

const [playerBoard, setPlayerBoard] = useState(GameBoard('player'));
// No use effect after this

If creating the gameboard is synchronous but expensive, you can instead pass a function into useState, and that function will only be called on the first render:

const [playerBoard, setPlayerBoard] = useState(() => GameBoard('player'));

If creating the game board is asynchronous, then you're right to use an effect, but there is no way to avoid the first render not having data. You will just need to make sure your component can work when it doesn't have data. A common way to do this is to start with the state being an value that indicates no data, and then check for it and return a placeholder. null's easier to check for than an empty object, so i'd recommend that:

const [playerBoard, setPlayerBoard] = useState(null);
    
useEffect(() => {
  // some asynchronous stuff, followed by a call to setPlayerBoard
},[])

if (!playerBoard) {
  return <div>Loading...</div>
  // Or if you don't want to show anything:
  // return null;
}

return (
  <div className='board-container'>
    // etc
  </div>
);

Upvotes: 4

Related Questions