First, as you are only using the name
field stored in your document, consider switching out groupSnapshot.data().name
for groupSnapshot.get("name")
for efficiency.
As this function is stateless, you should place this function outside of your component's render function:
const getPlayersByLobby = async (lobbyId) => {
const playersRef = firebase.firestore()
.collection("GameLobbies")
.doc(lobbyId)
.collection("players");
const playerNames = [];
const players = await playersRef.get();
players.forEach((groupSnapshot) => {
playerNames.push(groupSnapshot.get("name"))
});
console.log(playerNames) // ["player1Name", "player2Name"];
return playerNames
}
Same with this "render lobby" function:
const renderLobby = (lobby, playerList = undefined) {
let players;
if (playerList === undefined) {
players = (<p key="loading">Loading player list...</p>);
} else if (playerList === null) {
players = (<p key="failed">Failed to get player list</p>);
} else if (playerList.length === 0) {
players = (<p key="empty">Player list empty</p>);
} else {
players = (<ul key="players">{
playerList.map((p) => (
<li key={p.id}>
{p.name}
</li>
))
}</ul>);
}
return (
<div key={lobby.id}>
<h2>naam: {lobby.name}</h2>
<p>Code: {lobby.gameId}</p>
<h3>Spelers in deze lobby:</h3>
{ players }
</div>
);
}
Next, in your component, you make use of useEffect()
to fetch data from the database. Here, I've used a state object that is a map of lobby IDs to lists of players. On each render, a call is sent off to the database to fetch the current player list.
Because Promises (in this case, database calls) can take longer than render cycles to finish, we need to make sure to not call any setState
functions after the component has been unmounted. This is done using the disposed
boolean value to discard the results of the Promises if the useEffect
's unsubscribe function has been called.
const [gameLobbies, setGameLobbies] = useState(/* init value */);
const [playersByLobby, setPlayersByLobby] = useState({});
useEffect(() => {
let disposed = false;
const newPlayersByLobby = {};
Promise.all(gameLobbies.map((lobby) =>
getPlayersByLobby(lobby.id)
.then((playerList) => newPlayersByLobby[lobbyId] = playerList)
.catch((err) => {
console.error(`Failed to get Lobby #${lobbyId}'s player list: `, err);
newPlayersByLobby[lobbyId] = null;
})
))
.then(() => {
if (disposed) return; // component disposed/gameLobbies was changed
setPlayersByLobby(newPlayersByLobby);
})
return () => disposed = true;
}, [gameLobbies]); // rerun when gameLobbies updates
return (
<div className="App">
<h1>Current game lobbies:</h1>
{
gameLobbies.map(
(lobby) => renderLobby(lobby, playersByLobby[lobby.id])
)
}
</div>
);
Note: Consider implementing a <Lobby>
component along with a <PlayerList>
component. By doing this, you allow yourself the ability to make full use of the Firestore's Realtime updates.