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.