react-firebase-hooks
react-firebase-hooks copied to clipboard
Pagination Example
Question: is there any way to paginate through firebase list using this library?
At the moment there isn't, but this does seem like a very good use case.
Do you have any thoughts on what this might look like from an API point of view?
I will have a think, and put together a plan, but welcome your suggestions...
I will look into it and attempt to come up with something.
Did a bit of a research and firebase queries are very limiting in what can be done.
https://github.com/deltaepsilon/firebase-paginator/blob/master/firebase-paginator.js
^ Above would probably be the most comprehensive library that somewhat solved this issue.
I used hooks to filter data from firestore, we can extend that to implement pagination. Should I raise a PR for that?
@kriss1897 Yes, please do - I'd be interested to see how you've approached it...
@chrisbianca. Okay. Give me a few days.. I'll work on it this weekend.
Anyone ever get a pagination example working?
@kriss1897
i wrote a custom hook to do "Load More" type of pagination. i'm sure it can use some tweaking from more clever folks here, but it works fine for my purposes for now.
import { useState, useEffect, useCallback } from 'react'
import { useCollectionOnce } from 'react-firebase-hooks/firestore'
const useFirestoreLoadMore = queryFn => {
const [query, setQuery] = useState(null)
const [last, setLast] = useState(null)
const [data, setData] = useState([])
const [qData, loading, error] = useCollectionOnce(query)
useEffect(() => {
setData([])
setQuery(queryFn())
}, [queryFn])
useEffect(() => {
if (qData && qData.query.isEqual(query)) {
setLast(qData.docs[qData.docs.length - 1])
setData([...data, ...qData.docs])
}
}, [qData])
const more = useCallback(() => {
setQuery(queryFn().startAfter(last))
}, [queryFn, setQuery, last])
return [[data, loading, error], more]
}
export default useFirestoreLoadMore
you would use it like this:
const MyComponent = () => {
const queryFn = React.useCallback(() => {
let q = firebase
.firestore()
.collection('things')
.orderBy('date', 'desc')
.limit(15)
if (maybeYouWantToChangeSomething) {
q = q.where('something', 'array-contains', maybeYouWantToChangeSomething)
}
return q
}, [maybeYouWantToChangeSomething])
const [[things, loading, error], more] = useFirestoreLoadMore(queryFn)
return (
<div>
{things.map(thing => <Thing key={thing.id} data={thing} />)}
<button onClick={() => more()}>Load More</button>
</div>
)
}
i hope this is useful to someone.
now for Previous/Next pagination i've run into some roadblocks:
- i tried tracking
first
andlast
snapshot, hoping to usequery.startAfter(last)
for "Next" andquery.endBefore(first)
for "Previous" butendBefore
doesn't work the way you think combined withlimit()
. seems you have to track allfirst
snapshots for every page and use something likequery.startAfter(firstOfPreviousPage)
for "Previous" - i can't think of a way to know there is no more "Previous" pages other than tracking a separate
page
state (i.e. disable "Previous" ifpage === 0
). - not sure how to know we've reached the last page, it seems you need to try to load the next page, and if no results (or less than your limit) then that's the last page. seems like a lot of logic and i feel there might be a simpler way
would love to know how you guys are approaching it.
Another attempt https://github.com/bmcmahen/firestore-pagination-hook
Can confirm that @RobertSasak's package works well. Thanks!
I created an example of hook with react.js for pagination https://gist.github.com/robsonkades/0f256ab05944699b831031c7e6a8aa84
I found this one https://github.com/kodai3/react-firebase-pagination-hooks
any update from react-hooks
contribute?
any update from
react-hooks
contribute?
i'm looking for that too
I actually whipped this up before having seen this Github page, and I came here to share my snippet, but those other hooks look good too.
Here's what I came up with:
import { FirebaseFirestoreTypes } from "@react-native-firebase/firestore";
import { useCallback, useEffect, useMemo, useState } from "react";
const PAGE_SIZE = 20;
export const useCollectionDataStatic = (query: FirebaseFirestoreTypes.Query) => {
const [data, setData] = useState([]);
const [fetching, setFetching] = useState(false);
const dataVals = useMemo(() => {
return data.map(doc => doc.data());
}, [data])
const loadMore = useCallback(() => {
if (fetching) {
return;
}
if (data.length === 0) {
setFetching(true);
query.limit(PAGE_SIZE).get().then(result => {
setData([...result.docs])
}).catch(err => console.log(err)).finally(() => setFetching(false));
} else {
setFetching(true);
query.startAfter(data[data.length - 1]).limit(PAGE_SIZE).get().then(result => {
setData([...data, ...result.docs]);
}).catch(err => console.log(err)).finally(() => setFetching(false));
}
}, [data, dataVals, fetching]);
useEffect(() => {
loadMore();
}, []);
return [data, dataVals, loadMore];
};
Then it was pretty easy to use it:
const [invoices, invoiceDatas, loadMoreInvoices] = useCollectionDataStatic(query);
return (<FlatInvoiceList invoiceList={invoiceDatas} onEndReached={loadMoreInvoices}/>);
Any updates on this feature?
I will start to contribute here with the pagination @chrisbianca. Anyone here knows if already have some progress about that?
I have a lot of experience with firebase realtime database, with complex queries, compound indexes, "rules and typescript synchronization" and pagination. I think that pagination will have similar Params Interface between realtime database and firestore. So, I can start by doing pagination.
@chrisbianca Do you want to help me with prioritization here (rtdb or firestore or to launch both together)?
PS: I want to share my personal progress through this library to reward the open-source community, especially that. I've created an Issue with my idea explained #219
Hello !!!!!!! :). i made my own hook
Componente
const queryFn = useCallback(() => { const q = query( collection(fireStore, "admin_news"), limit(4), orderBy("categoryValue") ); return q; }, []);
const { more, isLoading, data } = usePaginateQuery(queryFn);
Create hook
import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { DocumentData, Query } from "firebase/firestore"; import { startAfter, query, getDocs } from "firebase/firestore";
const usePaginateQuery = (queryFn: () => Query<DocumentData>) => {
const [data, setData] = useState([]);
const isMountedRef = useRef(false);
const lastItemRef = useRef(null);
const [isLoading, setisLoading] = useState
const resetStates = () => { setData(null); setData([]); setisLoading(false); };
useEffect(() => { if (isMountedRef.current === true) return;
async function fetchQuery() {
try {
isMountedRef.current = true;
setisLoading(true);
const q = query(queryFn());
const querySnapshot = await getDocs(q);
setData([...querySnapshot.docs]);
lastItemRef.current = querySnapshot.docs[querySnapshot.docs.length - 1];
setisLoading(false);
} catch (error) {
resetStates();
setErrorMsg(error.code);
}
}
fetchQuery();
}, [queryFn]);
const more = useCallback(async () => { try { setisLoading(true); const next = query(queryFn(), startAfter(lastItemRef.current)); const querySnapshot = await getDocs(next); setData([...data, ...querySnapshot.docs]); lastItemRef.current = querySnapshot.docs[querySnapshot.docs.length - 1]; setisLoading(false); } catch (error) { resetStates(); setErrorMsg(error.code); } }, [data, queryFn]);
return useMemo( () => ({ more, isLoading, data, errorMsg, }), [more, isLoading, data, errorMsg] ); };
export default usePaginateQuery;