Search code examples
reactjsgoogle-cloud-firestorereact-hooksnext.jsapi-design

Next.js/Firebase - How to create infinite scroll?


I am a little lost on how to create infinite scroll elements in next.js with firebase firestore.

I am trying to use "my api" and use firestore on my api files.

My call to api is like this for now. I have a list called data, I send a request to my api and get the first 9 (limit param) docs.

useEffect(() => {
  console.log("useEffect");
  const getImage = async () => {
    const req = await fetch(`api/firebase?limit=9`, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
    });
    const data = await req.json();
    return data;
  };
  getImage().then((data) => {
    setData(data);
  });
}, []);

And my request handling is like this:

if (req.method === "GET") {
  const qu = req.query;
  const max = qu.limit;
  const q = query(collection(db, "images"), limit(max));
  const querySnapshot = await getDocs(q);
  const data = querySnapshot.docs.map((doc) => {
    return {
      label: doc.data().label,
      url: doc.data().url,
      id: doc.id,
    };
  });
  res.status(200).json(data);
}

I have tried/ looked up to react-infinite-scroll-component and I think I can make it work but I am lost on how to get the next 9 docs with my api from firebase.

On firestore there's startAfter() which takes documentSnapshot not an id but I can work with that by saving the id from the last doc send and calling it with getDoc with that id and start after that, but I don't know how to implement that thought with my api calling. Let's say there are 27 docs on my firestore, I call that first 9 with useEffect, do I need another function to call and update my data? I could try sending the last id as param and update but that gets complicated and I don't know if there's a simple way to this.


Solution

  • If anyone else stumble upon such problem here's how i solved it

    I added this if statement to my api handler which checks if i sent docID param via call and gets the document with that id, after that i sent that document to startAfter.

    if (qu.docID) {
      const id = qu.docID;
      const max = qu.limit;
    
      const idquery = query(
        collection(db, "images"),
        where("__name__", "==", id)
      );
    
      const documentSnapshots = await getDocs(idquery);
      const lastVisible =
        documentSnapshots.docs[documentSnapshots.docs.length - 1];
    
      const next = query(
        collection(db, "images"),
        orderBy("createdAt", "desc"),
        startAfter(lastVisible),
        limit(max)
      );
      const querySnapshot = await getDocs(next);
      const data = querySnapshot.docs.map((doc) => {
        return {
          label: doc.data().label,
          url: doc.data().url,
          id: doc.id,
        };
      });
      res.status(200).json(data);
    }
    

    Like Jose German said i created another function for loading more

    const getNext = async (docID) => {
    console.log("getNext");
    const req = await fetch(`api/firebase?limit=9&docID=${docID}`, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
    });
    
    const data = await req.json();
    return data;
    };
    

    Lasly i used react-infinite-scroll-component's next={} to call my second function and after few tweaks it worked