0

I want to get some data from firestore and then display it in JSX style. This is why I have used useEffect() in the following code as I want all the promises to be resolved and the data to be in the variable before I return my react component. This component works when I manually define the data as an object in javascript so the error lies in firestore returning an empty array, not the react code.

This is my programme :

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";

import { addDoc, collection, onSnapshot } from "firebase/firestore";
import { db } from "../data/firestore-data";

import { UserAuth } from "../context/AuthContext";

export default function ProductPage() {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    const productsRef = collection(db, "products");

    getDocs(productsRef).then((res) => {
      const ords = res.docs.map((doc) => ({
        data: doc.data(),
        id: doc.id,
      }));

      const products = ords.map((ord) => ({
        id: ord.id,
        name: ord.data.name,
        price: ord.data.price,
        imageSrc: ord.data.imageSrc,
        imageAlt: ord.data.imageAlt,
        productDescription: ord.data.productDescription,
      }));

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

  const productID = useParams().id;
  console.log(productID);

  const [count, setCount] = useState(1);

  const incrementCount = () => {
    setCount(count + 1);
  };

  const decrementCount = () => {
    if (count >= 2) {
      setCount(count - 1);
    }
  };

  console.log("Template : ");
  console.log(products);
  const product = products.find((obj) => obj.id === productID);
  const productName = product.name;
  const productPrice = product.price;
  const img = product.imageSrc;
  const productDescription = product.productDescription;

  const imgSize = 600;

  const { user } = UserAuth();

  function AddOrder() {
    console.log("Working...");
    const ordersRef = collection(db, "orders");
    addDoc(ordersRef, {
      ProductID: productID,
      UserID: user.uid,
    }).then((res) => {
      console.log(res);
    });
  }

  return (
    <React.Fragment>
      <div className="m-[70px] ml-[130px] flex flex-row items-start font-mono">
        <img
          className="mt-0 rounded-lg"
          style={{ width: imgSize, height: imgSize }}
          src={img}
          alt={productName}
        />
        <div className="mx-[90px] flex-grow space-y-9">
          <h1 className="text-5xl">{productName}</h1>
          <p className="text-lg font-bold">Available At: ${productPrice}</p>
          <p className="text-lg">{productDescription}</p>
          <div className="flex space-x-3">
            <div className="inline-flex h-[50px] flex-grow basis-1/4 items-center justify-center space-x-4 py-2 px-4">
              <button
                className="h-[25px] w-[25px] border-2 border-solid border-orange-600 text-center hover:bg-orange-600"
                onClick={decrementCount}
              >
                -
              </button>
              <span className="text-center text-xl text-black">{count}</span>
              <button
                className="h-[25px] w-[25px] border-2 border-solid border-orange-600 text-center hover:bg-orange-600"
                onClick={incrementCount}
              >
                +
              </button>
            </div>
            <button className="h-[50px] flex-grow basis-3/4 items-center rounded bg-orange-600 py-2 px-4 hover:bg-orange-500">
              <button
                className="text-center text-xl font-bold text-white"
                onClick={AddOrder}
              >
                Buy Now
              </button>
            </button>
          </div>
        </div>
      </div>
    </React.Fragment>
  );
}

When that didn't work I tried with the onSnapshot() function as well but it returned the same empty array. My code for that was :

 useEffect(() => {
    const productsRef = collection(db, "products");
    onSnapshot(productsRef, (snapshot) => {
      setProducts(
        snapshot.docs.map((doc) => ({
          id: doc.id,
          name: doc.data.name,
          price: doc.data.price,
          imageSrc: doc.data.imageSrc,
          imageAlt: doc.data.imageAlt,
          productDescription: doc.data.productDescription,
        }))
      );
    });
  }, []);

This is an image of the console (the errors are because I'm trying to access values of an object that's an empty array but they'll get solved when the empty array part does). The error message and empty array is the same for the both getDocs() and onSnapshot(): enter image description here

If you need any context or the code of some of the files I have imported please let me know by commenting on this question.

Edit: Added image of the console

2
  • Can you check this thread? Commented Mar 11, 2023 at 12:22
  • I don't understand.. How do I return a promise because 1. useEffect does not act like a function and 2. Even if I was able to, how do I return the promise directly? Commented Mar 11, 2023 at 14:38

1 Answer 1

1

The problem seemed to be with defining constants that referred to the object that was supposed to be resolved through the firestore promise (i.e. the data I wanted to retrieve). To solve this, all I had to do was add a map and then an if condition which seem redundant because what's the point of looping through the data when there's only one I want but map only renders/returns the object when the promise is resolved. It's a messy solution, but still a valid one. If anyone has a more efficient way, please feel free to post an answer to the question but for now I am marking my answer (this) as the correct one. The following is the amended code :

import { addDoc, collection, getDocs } from "firebase/firestore";
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import Footer from "../components/Footer";
import { UserAuth } from "../context/AuthContext";
import { db } from "../data/firestore-data";

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

  useEffect(() => {
    const productsRef = collection(db, "products");

    getDocs(productsRef).then((res) => {
      const ords = res.docs.map((doc) => ({
        data: doc.data(),
        id: doc.id,
      }));

      const products = ords.map((ord) => ({
        id: ord.id,
        name: ord.data.name,
        price: ord.data.price,
        imageSrc: ord.data.imageSrc,
        imageAlt: ord.data.imageAlt,
        productDescription: ord.data.productDescription,
      }));

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

  const productID = useParams().id;
  console.log(productID);

  const { user } = UserAuth();

  function AddOrder() {
    console.log("Working...");
    const ordersRef = collection(db, "orders");
    addDoc(ordersRef, {
      ProductID: productID,
      UserID: user.uid,
    }).then((res) => {
      console.log(res);
    });
  }

  const [count, setCount] = useState(1);

  const incrementCount = () => {
    setCount(count + 1);
  };

  const decrementCount = () => {
    if (count >= 2) {
      setCount(count - 1);
    }
  };

  return (
    <React.Fragment>
      {products.map((product) => {
        if (product.id === productID) {
          return (
            <div className="m-[70px] ml-[130px] flex flex-row items-start font-mono">
              <img
                className="mt-0 rounded-lg"
                style={{ width: 600, height: 600 }}
                src={product.imageSrc}
                alt={product.name}
              />
              <div className="mx-[90px] flex-grow space-y-9">
                <h1 className="text-5xl">{product.name}</h1>
                <p className="text-lg font-bold">
                  Available At: ${product.price}
                </p>
                <p className="text-lg">{product.productDescription}</p>
                <div className="flex space-x-3">
                  <div className="inline-flex h-[50px] flex-grow basis-1/4 items-center justify-center space-x-4 py-2 px-4">
                    <button
                      className="h-[25px] w-[25px] border-2 border-solid border-orange-600 text-center hover:bg-orange-600"
                      onClick={decrementCount}
                    >
                      -
                    </button>
                    <span className="text-center text-xl text-black">
                      {count}
                    </span>
                    <button
                      className="h-[25px] w-[25px] border-2 border-solid border-orange-600 text-center hover:bg-orange-600"
                      onClick={incrementCount}
                    >
                      +
                    </button>
                  </div>
                  <button className="h-[50px] flex-grow basis-3/4 items-center rounded bg-orange-600 py-2 px-4 hover:bg-orange-500">
                    <button
                      className="text-center text-xl font-bold text-white"
                      onClick={AddOrder}
                    >
                      Buy Now
                    </button>
                  </button>
                </div>
              </div>
            </div>
          );
        }
      })}
      <Footer />
    </React.Fragment>
  );
}

export default Home;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.