nandorojo / swr-firestore

Implement Vercel's useSWR for querying Firestore in React/React Native/Expo apps. 👩‍🚒🔥

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question: Pagination with loading

thirdman opened this issue · comments

Normally, I can show placeholder content while data is loading:
if (!data) return <Text>Loading...</Text>

I'm wondering if there's a nice inbuilt way of doing this for the pagination example (before I roll my own), since data already exists.

Regardless, Thanks for this project, it's working well for me!

Hmm, good question. The best I have in mind right now is to just use a paginating state variable. Maybe you could wrap the hook and make usePaginatableCollection yourself?

Thanks.

If people with bigger brains than me come up with a nice solution, I will happily use it. 🤣

Sounds good haha

Does the solution I proposed make sense, or do you need a code sample?

I made this custom hook with one of my team members @devdivdev , maybe you can use it @thirdman

import { useEffect, useState } from 'react';
import { fuego, useCollection } from '@nandorojo/swr-firestore';
import {
OrderByDirection,
Query,
QueryDocumentSnapshot,
WhereFilterOp,
} from '@firebase/firestore-types';

type Props = {
collectionPath: string;
orderBy: [string, OrderByDirection][];
where: [string, WhereFilterOp, string][];
maxRows: number;
};

const getMoreDocs = async (query: Query | null, startAfterDocument?: QueryDocumentSnapshot) => {
if (!query) {
return;
} else {
return await query
.startAfter(startAfterDocument)
.get()
.then(d => {
const docs: any[] = [];

    d.docs.forEach(doc => {
      docs.push({ ...doc.data(), id: doc.id, __snapshot: doc });
    });
    return docs;
  });

}
};

const createQuery = ({ collectionPath, where, orderBy, maxRows }: Props) => {
const ref = fuego.db.collection(collectionPath);

let query = where.reduce((res, w) => res.where(...w), ref);

query = orderBy.reduce((res, o) => res.orderBy(...o), query);

query = query.limit(maxRows);

return query;
};

export default function usePaginateCollection<T extends {}>({
collectionPath,
orderBy,
where,
maxRows,
}: Props) {
const [loading, setLoading] = useState(false);
const [empty, setEmpty] = useState(false);
const [firstLoad, setFirstLoad] = useState(false);
const [query, setQuery] = useState<Query | null>(null);

const handleFinish = () => {
setFirstLoad(true);
};

const { data, mutate } = useCollection(
collectionPath,
{
orderBy,
where,
limit: maxRows,
ignoreFirestoreDocumentSnapshotField: false,
},
{
revalidateOnFocus: false,
refreshWhenHidden: false,
refreshWhenOffline: false,
refreshInterval: 0,
onSuccess: handleFinish,
},
);

useEffect(() => {
if (firstLoad && data && data.length < maxRows) {
setEmpty(true);
}
}, [data, firstLoad, maxRows]);

useEffect(() => {
setQuery(createQuery({ collectionPath, orderBy, where, maxRows }));
}, [collectionPath, maxRows, orderBy, where]);

const paginate = async () => {
if (!data?.length) return;
setLoading(true);
const startAfterDocument = data[data?.length - 1].__snapshot;
const moreDocs = await getMoreDocs(query, startAfterDocument);

if (!moreDocs?.length) {
  setEmpty(true);
} else {
  if (moreDocs.length < maxRows) {
    setEmpty(true);
  }
  mutate((state: any) => [...state, ...moreDocs], false);
}
setLoading(false);

};

return { loading, data, paginate, empty };
}

@nandorojo Thanks, I'm only just now getting back to think about it.

@Naimdasb So it's a reusable chunk? I like this sort of approach.

I'm using this with nextjs, so I'm gonna have to think about whether that messes with things. 🤔

Thanks both of you. I appreciate the responses.

Yes, I'm using it with next js.