Reputation: 25
I am making a chat application in ReactJS using Firebase.
Everything works fine, but I ran into some trouble whilst trying to use pagination
I have looked into this and that worked fine: Pagination using react-firebase-hooks The problem is that once I send/load a new message it reloads the entire data and duplicates it.
Then afterwards I tried switching to the Realtime database using useList
instead of useCollection
Using useList
it sort of works, but it replaces the top element with the bottom one, and therefore removes already loaded information. So instead of appending the list, it's just adjusting the references.
I saw another method being used with two seperate lists, one for old messages and one for new messages. But this doesn't look like the most efficient solution...
Other question: [Accepted Answer] using two seperate lists.
Chat.js
const users = [props.uid, auth.currentUser.uid]
users.sort()
const conversationReference = db.collection('conversations').doc(users[0] + "_" + users[1])
const queryFn = React.useCallback(() => {
let q = conversationReference
.collection('messages')
.orderBy('createdAt', 'desc')
.limit(20)
return q
}, [props.uid])
const [[messages, queryLoading, error], more] = useFirestoreLoadMore(queryFn)
const sendChat = () => {
if(message.length > 0) {
chatReference.add({
to: props.uid,
from: auth.currentUser.uid,
message: message,
createdAt: new Date(),
}).then((docRef) => {
conversationReference.update({
lastChat: message,
lastActivity: new Date()
}).then(() => {
setMessage('')
})
}).catch((error) => {
console.log("Error sending message")
})
}
}
useEffect(() => {
if(top && messages[messages.length - 1].data().message)
more()
}, [top])
messages[messages.length - 1].data().message
: using dummy document to indicate end of collection
useFirestoreLoadMore.js
import {useCollection, useCollectionData, useCollectionOnce} from 'react-firebase-hooks/firestore'
export const useFirestoreLoadMore = queryFn => {
const [query, setQuery] = useState(null)
const [last, setLast] = useState(null)
const [data, setData] = useState([])
const [qData, loading, error] = useCollection(query)
useEffect(() => {
setData([])
setQuery(queryFn())
}, [queryFn])
useEffect(() => {
if (qData && qData.query.isEqual(query)) {
setLast(qData.docs[qData.docs.length - 1])
//console.log("first: ", data.length, " | second: ", qData.docs.length)
setData([...data, ...qData.docs])
}
}, [qData])
const more = useCallback(() => {
setQuery(queryFn().startAfter(last))
}, [queryFn, setQuery, last])
return [[data, loading, error], more]
}
Question: How do I paginate the [messages]
of useList
or useCollection
(I'm open to both methods. Haven't fully decided which one suits best) whilst also being able to receive new messages?
Upvotes: 1
Views: 1122
Reputation: 25
I settled on changing the useFirestoreLoadMore
:
useFirestoreLoadMore.js
export const useFirestoreLoadMore = queryFn => {
const baseValue = 20
const increment = 100
const [query, setQuery] = useState(null)
const [limit, setLimit] = useState(baseValue)
const [data, setData] = useState([])
const [qData, loading, error] = useCollection(query)
useEffect(() => {
setData([])
setQuery(queryFn().limit(limit))
}, [queryFn])
useEffect(() => {
if (qData && qData.query.isEqual(query)) {
setData([...qData.docs])
}
}, [qData])
const more = useCallback(() => {
setQuery(queryFn().limit(limit + increment))
setLimit(limit + increment)
}, [queryFn, setQuery])
return [[data, loading, error], more]
}
It's not the most efficient way I assume. But I didn't see another way to maintain live updates regardless..
Upvotes: 1