Rather than nest everything in your getGamesInSelectedGroup
function (which is the pattern you would use for Promise-based APIs), just call it in the body of the useEffect
to make managing the listener simpler:
useEffect(() => {
if (!state.currentGroup) {
return
}
const db = getDatabase();
const resp = ref(db, `/games/${state.currentGroup.name}`);
return onValue(resp, (snap) => { // <--- return the unsubscriber!
if (snap.exists()) {
const data = snap.val()
const games = Object.keys(data)
.map(k => ({
id: k,
group: state.currentGroup.name,
...data[k]
}));
setState((prev) => ({
...prev,
games, // you can use this instead of "games: games"
isLoaded: true,
}));
return
}
setState((prev) => ({
...prev,
games: null,
isLoaded: true,
}));
toast.warning("no data for " + state.currentGroup.name)
});
}, [state.currentGroup])
I would also recommend using a “snapshot to array of children” function rather than using Object.keys(snapshot.val())
to maintain the sort order from the query (it would be ignored using the code as-is). Unfortunately, at the time of writing, there is no equivalent of Firestore’s QuerySnapshot#docs
for the RTDB just yet. But it’s pretty easy to make our own:
// returns array of DataSnapshot objects under this DataSnapshot
// put this outside of your component, like in a common function library file
const getSnapshotChildren = (snapshot) => {
const children = [];
// note: the curly braces on the next line are important! If the
// callback returns a truthy value, forEach will stop iterating
snapshot.forEach(child => { children.push(child) })
return children;
}
useEffect(() => {
if (!state.currentGroup) {
return
}
const db = getDatabase();
const resp = ref(db, `/games/${state.currentGroup.name}`);
return onValue(resp, (snap) => { // <--- return the unsubscriber!
if (snap.exists()) {
const games = getSnapshotChildren(snap)
.map(child => ({
id: child.key,
group: state.currentGroup.name,
...child.val()
}));
setState((prev) => ({
...prev,
games, // you can use this instead of "games: games"
isLoaded: true,
}));
return
}
setState((prev) => ({
...prev,
games: null,
isLoaded: true,
}));
toast.warning("no data for " + state.currentGroup.name)
});
}, [state.currentGroup])
CLICK HERE to find out more related problems solutions.