skip to Main Content

I am following along with a netflix clone project off of youtube that was put out about 6mo ago. Since the tutorial came out I believe firebase/firestore and react have released newer versions of themselves which seems to be causing the tutorial code to break. Here is what the tutorial wants us to use:

```import React, { useState, useEffect } from 'react';
import db from "../firebase";
import './PlansScreen.css';

function PlansScreen() {
    const [products, setProducts] = useState([]);

    useEffect(() => {

     db.collection("products")
     .where("active", "==", true)
     .get()
     .then(querySnapshot) => {
       const products = {};
       querySnapshot.forEach(async (productDoc) => {
       products[productDoc.id] = productDoc.data();
       const priceSnap = await productDoc.ref.collection
       ("prices").get();
       priceSnap.docs.forEach((price) => {
         products[productDoc.id].prices = {
           priceId: price.id,
           priceData: price.data(),
         };
       });
     });
     setProducts(products);
    });
 }, []);

    console.log(products)

    return (
        <div className='plansScreen'>

        </div>
    )
}

export default PlansScreen```

What this code block is supposed to do is go into the collection named Products, create Product objects containing each Product Document’s data, then go into each Product Document’s subcollection called Prices, grab the data from each corresponding Price document, and add the data to to its Product object. Then the Product objects are logged to the console. The codeblock above does work unfortunately. HOWEVER I was able to refactor the code somewhat to be current with firebase/firestore and REACT.js which does do some of what it needs to. Here is my refactor:

```import React, { useState, useEffect } from 'react';
import db, { collection, getDocs } from "../firebase";
import './PlansScreen.css';

function PlansScreen() {
    const [products, setProducts] = useState([]);

    useEffect(() => {

        const get = async () => {

            const prodRef = collection(db, 'products');
            const prodSnap = await getDocs(prodRef);

            prodSnap.forEach(async (prodDoc) => {
                products[prodDoc.id] = prodDoc.data();

            });
            setProducts(products);
        };
        get();
    }, [products]);

    console.log(products)

    return (
        <div className='plansScreen'>

        </div>
    )
}

export default PlansScreen```

as you can see I had to refactor the way I was importing firebase and some firebase tools. My refactored code block does log each Product object in the console but it does not include the data from the prices subcollection. That is my currrent issue. I have tried many things but cannot seem to figure out how to access the subcollection to grab data during the process of creating the product objects. Hopefully someone can show me how to do it because all the stackoverflow answers are from awhile ago and those methods don’t seem to work anymore.

2

Answers


  1. 1) You have a mistake when declaring priceSnap.

    In Firebase SDK 9, this is how you should get all documents in a collection:

    const priceSnap = await getDocs(collection(db, "prices"));
    

    2) await is used to resolve a promise, and it must be used inside an async function (or at a module top level). Another way of resolving promises is using .then(...).

    getDocs returns a promise and if you want to resolve it with await, it must be inside an async function. To do that inside a useEffect, just create an async function and call it. Inside the async function you can update your state with the fetched data.

    Using your latest example:

    function PlansScreen() {
      const [products, setProducts] = useState([]);
    
      useEffect(() => {
        const get = async () => {  // <= create async function
          const q = query(collection(db, "products"), where("active", "==", true));
    
          const querySnapshot = await getDocs(q);
    
          let data = []
          querySnapshot.forEach((doc) => {
            console.log(doc.id, "=>", doc.data());
            data.push({id: doc.id, product: doc.data()})
          });
    
          setProducts(data)      
        };
    
        get(); // <= call function
      }, [products]);
    }
    
    

    3) To query the "prices" collection, you could in theory follow the logic you were trying to implement.

    However, the logic of separating products and prices in different collections is not really the best option for Firestore, since it’s a non-relational database. You should denormalize your data, meaning the prices should be inside the products collection. If you do that, you won’t need a separate query for the prices.

    You can read more about denormalization here.

    Login or Signup to reply.
  2. I still your problem lies in the configuration file firebase.js, please refer to my configuration file.

    import firebase from "firebase/compat/app";
    import "firebase/compat/auth";
    import "firebase/compat/firestore";
    
    const firebaseConfig = {
      apiKey: "AIzaSyAcBqnloftIE-ELFOMnVjEJSYC9xKDyqrE",
      authDomain: "netflix-clone-ff255.firebaseapp.com",
      projectId: "netflix-clone-ff255",
      storageBucket: "netflix-clone-ff255.appspot.com",
      messagingSenderId: "622210570368",
      appId: "1:622210570368:web:3d39cad7dafb1ac3a6ae65",
    };
    
    // Use this to initialize the firebase App
    const firebaseApp = firebase.initializeApp(firebaseConfig);
    
    // Use these for db & auth
    const db = firebaseApp.firestore();
    const auth = firebase.auth();
    
    export { auth };
    export default db;
    

    This is your original code:

      const [products, setProducts] = useState([]);
    
      useEffect(() => {
        db.collection("products")
          .where("active", "==", true)
          .get()
          .then((querySnapshot) => {
            const products = {};
            querySnapshot.forEach(async (productDoc) => {
              products[productDoc.id] = productDoc.data();
              const priceSnap = await productDoc.ref.collection("prices").get();
              priceSnap.docs.forEach((price) => {
                products(productDoc.id).prices = {
                  priceId: price.id,
                  priceData: price.data(),
                };
              });
            });
            setProducts(products);
          });
      }, []);
    
      console.log("Products from db", products);
    

    I hope it will hellp you !!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search