1

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 Answers2

0

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.

  • I tried to strip the code down and make it simple for logging data in the console but it is not working. This is what I currently have which gives me an error "Parsing error: Unexpected reserved word 'await'. – Cameron Sickler Mar 01 '23 at 19:01
  • I updated the answer based on your new error. – Eduardo Motta de Moraes Mar 01 '23 at 20:53
  • I have it written like your example, not getting the errors anymore but it is not logging anything in the console. I am getting the following Warning: React Hook useEffect has a missing dependency: 'products'. Either include it or remove the dependency array. You can also do a functional update 'setProducts(p => ...)' if you only need 'products' in the 'setProducts' call react-hooks/exhaustive-deps – Cameron Sickler Mar 01 '23 at 21:11
  • You need to add products to the dependency array. I updated the answer with that. – Eduardo Motta de Moraes Mar 01 '23 at 21:40
  • Adding products to the dependency array worked! thank you. The final piece of this is accessing another collection called Prices within each Product. I have updated my question. – Cameron Sickler Mar 01 '23 at 22:54
  • That's a different question, but I edited my answer pointing you in the right direction. Also edited the code in my answer, as it had a mistake when using `setProduct` – Eduardo Motta de Moraes Mar 02 '23 at 12:10
  • I updated the question to reflect recent fixes. I only have one unresolved issue yet regarding accessing the data in a firestore subcollection. @Eduardo – Cameron Sickler Mar 09 '23 at 16:51
0

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 !!

  • I've tried the compat version of the Firebase imports and that does not seem to work. Did I do something wrong or is compat too old now? I was following along here when setting up Firebase in my app : https://firebase.google.com/docs/web/setup?authuser=0&hl=en – Cameron Sickler Mar 09 '23 at 16:49