skip to Main Content

So I am fetching the urls from a firestore database and then using those urls for the img tags.

However, the issue I am facing is that when the page loads the array is empty but if I make a change to the code ( any change at all even an empty space ) the array will then populate as the useeffect triggers again.. I would like for it to get the data on the first fetch.

First line of the console log is when the page loads, then the second line is when I added a random space to a div

enter image description here

import React, { useEffect, useState } from "react"
import { proFirestore } from "../firebase/config"
import { collection, onSnapshot, query } from "firebase/firestore"

const Photos = () => {
  const [imgs, setImgs] = useState([])

  const fetchAllImgs = async () => {
    try {
      const collectionRef = collection(proFirestore, "images")
      const qry = query(collectionRef)

      await onSnapshot(qry, (querySnapshot) => {
        setImgs(
          querySnapshot.docs.map((doc) => ({
            id: doc.id,
            data: doc.data(),
          }))
        )
      })
    } catch (error) {}
  }

  useEffect(() => {
    fetchAllImgs()
    console.log(imgs)
  }, [])

  return (
    <div className="absolute mt-[5%] flex w-full items-center justify-center">
      <div className="">
        <div>
          <button className="h-20 w-20   rounded bg-black text-white"></button>
        </div>
        <div>
          <img className="h-full w-full cursor-pointer" src={imgs[0]}></img>
        </div>
      </div>
    </div>
  )
}
export default Photos

I have tried many things such as .map() which works, but I want to only render a single item from the array based on its index..

Any help would be great?

2

Answers


  1. If you change it to this, does it work?

    const fetchAllImgs = async () => {
      try {
        const collectionRef = collection(proFirestore, "images");
        const qry = query(collectionRef);
    
        const querySnapshot = await getDocs(qry);
        const imgData = querySnapshot.docs.map((doc) => ({
          id: doc.id,
          data: doc.data(),
        }));
    
        setImgs(imgData);
      } catch (error) {
    
        console.error("Error fetching images:", error);
      }
    };
    
    

    or this:

    const fetchAllImgs = () => {
      const collectionRef = collection(proFirestore, "images");
      const qry = query(collectionRef);
    
      getDocs(qry)
        .then((querySnapshot) => {
          const imgData = querySnapshot.docs.map((doc) => ({
            id: doc.id,
            data: doc.data(),
          }));
          setImgs(imgData);
        })
        .catch((error) => {
          console.error("Error fetching images:", error);
        });
    };
    
    
    Login or Signup to reply.
  2. Each time the component renders this line:

    const [imgs, setImgs] = useState([])
    

    … reads the value out of the state and assigns it to imgs which is a constant.


    This chunk of code:

    useEffect(() => {
      fetchAllImgs()
      console.log(imgs)
    }, [])
    

    will, the first time (because the dependencies array is empty) the component renders will call fetchAllImgs and then log the value of imgs.

    fetchAllImgs calls setImgs which stores a new value in the state but it doesn’t change the value of imgs.

    Remember, imgs is a constant that is assigned a value at the top of the function rendering the component.


    Move the console.log outside of the effect.


    Also examine the log output of this example which keeps a count of renders:

    const App = () => {
      const [count, setCount] = useState(1);
      const [array, setArray] = useState([]);
    
      useEffect(() => {
        setCount(count => count + 1);
        setArray(['a', 'b', 'c']);
      }, [])
    
      console.log({count, array});
    
      return (
        <div>
          <p>Render count: {count}</p>
        </div>
      );
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search