DEV Community

Patrick Bradshaw
Patrick Bradshaw

Posted on

Firestore cross-reference returns a promise object, can't access document value [help]

I posted this on StackOverflow originally, but would welcome any suggestions:

Scenario

My app is built with React and Firebase. There is a feature in the app that allows users to create playlists and fill them with tracks from a catalog. A Playlist has a sub-collection called "Tracks" that stores the track ID's and some other info. When viewing a playlist, I want to loop through the tracks and cross-reference them (like a join in SQL) to the Tracks collection to populate the view.

// Looks something like this before the return...

// Attempting to write an async/await function
async function getTrackDocument(trackId) {
  const track = await firestore
    .collection("tracks")
    .doc(trackId)
    .get();
  return track;
}

useEffect(() => {
  const playlistRef = firestore.doc(`playlists/${playlistId}`);
  const playlistTracksRef = firestore.collection(
    `playlists/${playlistId}/tracks`
  );

  playlistRef.get().then(doc => {
    if (doc.exists) {
      const unsubscribe = playlistTracksRef.onSnapshot(snapshot => {
        // Snapshot of playlist tracks
        const playlistTracks = snapshot.docs.map(doc => {
          return {
            id: doc.id,
            ...doc.data(),
            // Getting the track document
            trackRef: getTrackDocument(doc.id).then(doc => {
              return doc.data();
            })
          };
        });

        setTracks(playlistTracks);
      });

      return () => unsubscribe();
    }
  });
});
Enter fullscreen mode Exit fullscreen mode

Problem

The part where I am trying to cross-reference a Playlist Track ID to the Tracks collection:

trackRef: getTrackDocument(doc.id).then(doc => {
   return doc.data();
})
Enter fullscreen mode Exit fullscreen mode

I was hoping that would give me the track information I needed. That part returns a promise object in the console:

trackRef: Promise
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Object
__proto__: Object
Enter fullscreen mode Exit fullscreen mode

And [[PromiseValue]] has all the values, but I can't get seem to get to where I can use the values. I can log doc.data() to the console and it shows me the values I need eventually. Is there a way to return those values in-line the way the code is written? I've tried different ways of writing async/await functions, and I also tried to return a Promise.resolve(data), always the same results.

The closest I've come to the behavior I was hoping for was by creating a function where I could grab the Track and just push it to tracks array in state, like:

// @param arrayOfTrackIDs -- just track IDs from the playlist
function getTracksThenPopulateArray(arrayOfTrackIDs) {
   arrayOfTrackIDs.forEach(trackID => {
      firestoreTracks.doc(trackID).get().then(doc => setTracks(tracks => [...tracks, track])); 
   }
}
Enter fullscreen mode Exit fullscreen mode

This wasn't ideal however because I couldn't incorporate .onSnapshot() to update the view if tracks were removed or added.

Discussion (0)