YouriHeuvel
YouriHeuvel

Reputation: 25

How can I have pagination and receive new messages simultaneously with Firebase?

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

Answers (1)

YouriHeuvel
YouriHeuvel

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

Related Questions