I'm working on a chat app and we have abilities to jump to older messages. So this is the current flow of how it works:
- When opening the chat room, the API will get called, fetching 25 newest messages store it in a global state called
messages. - As we scroll up, it will gradually fetch another 25 and so on... (Something like an infinite scroll).
For the WebSocket part of things, what I think that can be done is to append new messages to the messages state. So for sending message, it would look something like:
- Do an optimistic update to the
messagesstate with a temporaryid - Call the API to send a message and get the sent message as a response from BE
- Use that response to replace the optimistic message we sent in step 1.
And for receiving message, the flow looks something like:
- Get a message from WS that theres an incoming message, the WS message currently doesn't include the content of the message, only the
idof the new message, so what I have to do next - Get the new message using a function called
getMessageByIdand then use the response and append it to themessagesstate.
I feel like the approach above is fine until I try to access older messages. For e.g. I want to access message that was from 1st January last year (in this case 2023), the approach looks something like:
- Empty out the
messagesstate - Call an API
getMessagesFrom(certainDateHere) - Use the response and fill that
messagesstate.
This I think works fine, until I receive or send a new message, which will get appended wrongly. I was thinking of adding another state to keep track whether we have the newest messages in the state or not to decide to append the new messages or not. But I can't quite figure out how I should be doing it. Or another approach I thought off was to separate the messages state, but I haven't really given it much thought. I'm currently using ReactJS to build this chat app. If anyone has any recommendation on which approach should I be taking, it would be very helpful. Thank you so much.
Create message-buckets which hold lists of messages. When you first fire-up and have a set of real messages in the first bucket of the array, you prepend an empty message-bucket to the list, but set your scroll-point to last bucket - (scroll-length).
When the empty-bucket scrolls into view, do your lazy-load, and at the end pre-pend another empty-bucket as you scroll up.
New, incoming messages always get appended to the last bucket in the array (which holds most recent messages).
If user scrolls up to the top of the message-queue, add a new bucket to the top to populate the first 25, and leave the empty-bucket now in-between the top & bottom. Still, when it scrolls into view you need to fill in the next view-chunk (either up or down, depending on the previous bucket-boundary), but you will maintain a sparse data-structure until the user actually tries to access the messages in-between.
Regardless, this will let you have a lazy-loading of messages. Also you can batch-load in bucket-size (sounds like 25 or so, but might want to build buffer). Batch-loading will almost always be faster (especially at the database level).