Home > Software design >  React and Firebase Firestore V9 Previous Page Pagination returning to "first page"
React and Firebase Firestore V9 Previous Page Pagination returning to "first page"

Time:04-15

I'm at a loss here. I feel like I've been trying everything, and using the exact methods explained in other posts/tutorials everywhere. I understand that you need to use a cursor and set the first and last visible document so that you can start after the last, in the case of moving forward, and start BEFORE the first, in the case of moving backwards.

In my implementation, going forwards works fine. However, when I utilize the previousPage function, it returns me to the first page, despite setting the 'first visible' document. It returns to the first page even if I've already moved 3 'pages' forward.

Clearly there is something I'm not understanding here..

  const PAGE_SIZE = 6;
  const [posts, setPosts] = useState([]);
  const [lastVisible, setLastVisible] = useState(null);
  const [firstVisible, setFirstVisible] = useState(null);
  const [loading, setLoading] = useState(false);

  // Initial read to get first set of posts. 
  useEffect(() => {
    const q = query(
      collectionGroup(db, "bulletins"),
      orderBy("createdAt", "desc"),
      limit(PAGE_SIZE)
    );
    const unsubscribe = onSnapshot(q, (documents) => {
      const tempPosts = [];
      documents.forEach((document) => {
        tempPosts.push({
          id: document.id,
          ...document.data(),
        });
      });
      setPosts(tempPosts);
      setLastVisible(documents.docs[documents.docs.length - 1]);
      setFirstVisible(documents.docs[0]);
    });
    return () => unsubscribe();
  }, []);

  const nextPage = async () => {
    const postsRef = collectionGroup(db, "bulletins");
    const q = query(
      postsRef,
      orderBy("createdAt", "desc"),
      startAfter(lastVisible),
      limit(PAGE_SIZE)
    );
    const documents = await getDocs(q);
    updateState(documents);
  };

  const previousPage = async () => {
    const postsRef = collectionGroup(db, "bulletins");
    const q = query(
      postsRef,
      orderBy("createdAt", "desc"),
      endBefore(firstVisible),
      limit(PAGE_SIZE)
    );
    const documents = await getDocs(q);
    updateState(documents);
  };

  const updateState = (documents) => {
    if (!documents.empty) {
      const tempPosts = [];
      documents.forEach((document) => {
        tempPosts.push({
          id: document.id,
          ...document.data(),
        });
      });
      setPosts(tempPosts);
    }
    if (documents?.docs[0]) {
      setFirstVisible(documents.docs[0]);
    }
    if (documents?.docs[documents.docs.length - 1]) {
      setLastVisible(documents.docs[documents.docs.length - 1]);
    }
  };

CodePudding user response:

You should use endAt() instead of endBefore() and also, you should pass the order reference which is the createdAt to the endAt() method. See code below:

  const PAGE_SIZE = 6;
  const [posts, setPosts] = useState([]);
  const [lastVisible, setLastVisible] = useState(null);
  const [firstVisible, setFirstVisible] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const q = query(
      collectionGroup(db, "bulletins"),
      orderBy("createdAt", "desc"),
      limit(PAGE_SIZE)
    );
    const unsubscribe = onSnapshot(q, (documents) => {
      const tempPosts = [];
      documents.forEach((document) => {
        tempPosts.push({
          id: document.id,
          ...document.data(),
        });
      });
      setPosts(tempPosts);
      setLastVisible(documents.docs[documents.docs.length - 1]);
      setFirstVisible(documents.docs[0]);
    });
    return () => unsubscribe();
  }, []);

  const nextPage = async () => {
    const postsRef = collectionGroup(db, "bulletins");
    const q = query(
      postsRef,
      orderBy("createdAt", "desc"),
      startAfter(lastVisible.data().createdAt), // Pass the reference
      limit(PAGE_SIZE)
    );
    const documents = await getDocs(q);
    updateState(documents);
  };

  const previousPage = async () => {
    const postsRef = collection(db, "bulletins");
    const q = query(
      postsRef,
      orderBy("createdAt", "desc"),
      endAt(firstVisible.data().createdAt), // Use `endAt()` method and pass the reference
      limitToLast(PAGE_SIZE)
    );
    const documents = await getDocs(q);
    updateState(documents);
  };

  const updateState = (documents) => {
    if (!documents.empty) {
      const tempPosts = [];
      documents.forEach((document) => {
        tempPosts.push({
          id: document.id,
          ...document.data(),
        });
      });
      setPosts(tempPosts);
    }
    if (documents?.docs[0]) {
      setFirstVisible(documents.docs[0]);
    }
    if (documents?.docs[documents.docs.length - 1]) {
      setLastVisible(documents.docs[documents.docs.length - 1]);
    }
  };

For more information, See Add a simple cursor to a query.

  • Related