0

I am looking for a way to update specific items in my PagingDataAdapter from the Paging 3 library. The recommended way at the moment seems to be to invalidate the PagingSource but this causes the adapter to fetch the whole data set again, which is not efficient and also shows my loading spinner.

However, I noticed that I can access and modify items in the adapter using the peek() method and it seems to work quite well. Am I missing anything here? Will this fall apart in certain scenarios? I know that it's good practice to keep data classes immutable but this approach makes my life a lot easier. Here is an example of my usage and it seems to work quite well:

viewModel.chatMessageUpdateEvents.collect { messageEvent ->
    when (messageEvent) {
        is FirestoreChatMessageListener.ChatMessageUpdateEvent.MessageUpdate -> {
            val update = messageEvent.chatMessage
            val historyAdapterItems = chatMessagesHistoryAdapter.snapshot().items
            val updatedMessage =
                historyAdapterItems.find { chatMessage ->
                    chatMessage.documentId == messageEvent.chatMessage.documentId
                }
            if (updatedMessage != null) {
                val messagePosition = historyAdapterItems.indexOf(updatedMessage)
                chatMessagesHistoryAdapter.peek(messagePosition)?.unsent = update.unsent
                chatMessagesHistoryAdapter.peek(messagePosition)?.imageUrl = update.imageUrl
                chatMessagesHistoryAdapter.notifyItemChanged(messagePosition)
            }
        }
    }
}
Florian Walther
  • 6,237
  • 5
  • 46
  • 104

1 Answers1

1

I replied in a separate comment but wanted to post here for visibility.

This is really not recommended and a completely unsupported usage of paging.

One of the primary ways of restoring state if Pager().flow() hasn't been cleaned up (say if ViewModel hasn't been cleared yet) is via the .cachedIn(scope) method, which will cache out-of-date data in your case. This is also the only way to multicast (make the loaded data in PagingData re-usable) for usage in Flow operations like .combine() that allow you to mix transformations with external signals.

You'll also need to handle races between in-flight loads, what happens if you get a messageEvent the same time an append finishes? Who wins in this case and is it possible between taking the .snapshot() a new page is inserted so your notify position is no longer correct?

In general it's much simpler to have a single source of truth and this is the recommended path, so the advice has always been to invalidate on every backing dataset update.

There is an open FR in Paging's issue tracker to support Flow<Item> or Flow<Page> style data to allow granular updates, but it's certainly a farther future thing: https://issuetracker.google.com/160232968

dlam
  • 3,547
  • 17
  • 20