Temporary assigns with streams causes the order of items to be reversed when prepending


We have a page where we display events to users that relies on infinte scrolling. We decided to migrate from the old phx-update and temprory assigns fashion to LV Streams to leverage the :reset option to be able to clear the page when user makes a search with no results and everything works fine. Howerver, we are facing a strange issue when prepending new events with the option at: 0 of the stream function. This causes the order of items to be reversed on the client side ! the DB request does its job well with the right sorting order so why this option is reversing the order ? is it the normal behavior ? note that we didn’t have this issue before (with phx-update="prepend") and as a workaround is to use Enum.reverse() function :

stream(socket, :events, Enum.reverse(new_events), at: 0)

Thanks for your help

1 Like

I too am seeing this when specifying an at of anything other than -1. I suspect that what’s happening is that each item is being inserted separately at the index, causing a reverse order insert.

Filed: Bulk stream inserts at some indices inserts in reverse order · Issue #3023 · phoenixframework/phoenix_live_view · GitHub

Streams have had a few problems which have been actively worked on recently. This should already be fixed in LiveView 0.20.4 which will be released soon. Try using main and see if you still have the issue.

Also, welcome!

1 Like

Thanks for the tip. I tried it with {:phoenix_live_view, git: "https://github.com/phoenixframework/phoenix_live_view.git", branch: "main", override: true} and it is still reproducable.

Can you share the stream bits (with function context) of your code? I’m trying to reproduce in my project and haven’t been able to yet. I’m initializing in a handle_params thought that shouldn’t really affect anything. For example I have:

def handle_params(params, _uri, socket) do
  {:noreply, stream(socket, :artworks, artworks, reset: true)

def handle_info({:artwork_created, artwork}, socket) do
  {:noreply, stream_insert(socket, :artwork, artwork, at: -1)}

I’ve tried all sorts of permutations and they work as expected: I’ve gotten rid of the reset: true, I’ve added, at: 0 to stream, I replaced the stream_insert line with stream(socket, :artwork, [artworks]) (with and without :at) and I’ve gotten the expected results. I haven’t tried every permutation, of course, and would love to be able to reproduce this as stream woes have been a thing for me! (To be clear, I love the feature).

EDIT: OOPS disregard! :grimacing: :grimacing: :grimacing: I see you have a demo repo in the issue. Sorry!

@steffend already on the case :smiley:

It will probably only be a documentation change though :smile:

Couldn’t LV be smarter about this instead of needing to document that behaviour?

Oh ya, I saw. I started writing a response here that I felt this behaviour might be intentional but then I got myself into a mental loop of what I thought should be default so I backed out.

On one had status quo seems reasonable. If you are prepending to a list and doing so in intervals as events come in, I would assume that they would be prepended in the order they arrive, even when inserted in bulk. But then it is a bit surprising how stream acts one way when initializing and another when updating. And of course, it doesn’t make much sense they get “reversed” when inserting in the middle of a stream.

I feel like it would be far less confusing if there was an stream_insert_many that has the behaviour OP is looking for and stream would continue to act like it does. Not sure :thinking:

Well it could be, but I’ve been discussing this with Chris and José and the decision was to document it. Internally this is implemented as a reduce calling stream_insert on the items, so the decision was between making a special case or just documenting it. You can add your arguments to the PR though. It’s not accepted yet :smiley:

1 Like