DEV Community

Rossano D'Angelo
Rossano D'Angelo

Posted on

13 3

How to fetch subcollections from Cloud Firestore with React

More data!

First, I add more data to my database. Just to make things more realistic. For each cinema I add a subcollection movies in which I add some movies. Each movie has this info

name: string,
runtime: string,
genre: string,
release_date: timestamp
Enter fullscreen mode Exit fullscreen mode

In Firestore, data can also have different structure (NoSQL's power!) but, for simplicity, I follow the canonical way.

Alt Text

I add one movie for the first cinema and two movies for the second one.

Fetching the subcollection

I make the cinemas list clickable, so when I click an item I load movies scheduled for that specific cinema. To do this, I create a function selectCinema that will perform a new query to fetch a specific subcollection.

...
const selectCinema = (cinema) => {
  database.collection('cinemas').doc(cinema.id).collection('movies').get()
    .then(response => {
      response.forEach(document => {
        // access the movie information
      });
    })
    .catch(error => {
      setError(error);
    });
}
..
{cinemas.map(cinema => (
  <li key={cinema.id} onClick={() => selectCinema(cinema)}>
    <b>{cinema.name}</b> in {cinema.city} has {cinema.total_seats} total seats
  </li>
))}
Enter fullscreen mode Exit fullscreen mode

At this point is easy managing the show/hide logic with React using the state.

import React, { useState, useEffect } from 'react';
import firebase from 'firebase';
const firebaseConfig = {
apiKey: "AIzaSyDQXMsyejsUgPj-1ZPIyL9YMKdhZ280Mwo",
authDomain: "cinema-schedule-7bfa4.firebaseapp.com",
databaseURL: "https://cinema-schedule-7bfa4.firebaseio.com",
projectId: "cinema-schedule-7bfa4",
storageBucket: "cinema-schedule-7bfa4.appspot.com",
messagingSenderId: "215540682675",
appId: "1:215540682675:web:6e6e792cb9f041ae8e05c6"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const database = firebase.firestore();
const App = () => {
const [cinemas, setCinemas] = useState([]);
const [selectedCinema, setSelectedCinema] = useState();
const [movies, setMovies] = useState([]);
const [error, setError] = useState();
const selectCinema = (cinema) => {
setSelectedCinema(cinema);
database.collection('cinemas').doc(cinema.id).collection('movies').get()
.then(response => {
const fetchedMovies = [];
response.forEach(document => {
const fetchedMovie = {
id: document.id,
...document.data()
};
fetchedMovies.push(fetchedMovie);
});
setMovies(fetchedMovies);
})
.catch(error => {
setError(error);
});
}
const timestampToString = (timestamp) => {
return Date(timestamp).toString();
}
useEffect(() => {
database.collection('cinemas').get()
.then(response => {
const fetchedCinemas = [];
response.docs.forEach(document => {
const fetchedCinema = {
id: document.id,
...document.data()
};
fetchedCinemas.push(fetchedCinema);
});
setCinemas(fetchedCinemas);
})
.catch(error => {
setError(error);
});
}, []);
return (
<div>
{error ? (
<p>Ops, there is an error :(</p>
) : null}
<ul>
{cinemas.map(cinema => (
<li key={cinema.id} onClick={() => selectCinema(cinema)}>
<b>{cinema.name}</b> in {cinema.city} has {cinema.total_seats} total seats
</li>
))}
</ul>
{selectedCinema ? (
<ul>
{movies.map(movie => (
<li key={movie.id}>
<b>{movie.name}</b> | {movie.genre} | {movie.runtime} | {timestampToString(movie.release_date)}
</li>
))}
</ul>
) : null}
</div>
);
}
export default App;
view raw App.js hosted with ❤ by GitHub

A working demo

Gaunt but working.

Alt Text

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (2)

Collapse
 
gkranasinghe profile image
gkranasinghe

Associating a hook with query will cause infinite rerendering of the component ,as useState will rerender the component ,which will lead to reexecute the Query .I think this is really a bad practice

Collapse
 
lucianoschillagi profile image
Luko

Hello,
and what is your solution for this case?
Thanks!

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay