Infinite scrolling with LiveView?

I have tried several examples for implementing an ‘infinite scrolling feed’ using liveview, but none of them seem to work if a user is expected to navigate away from the feed at some point, and then come back to it again through the browser’s back button.

In all of the examples I have seen, the feed relies on temporary assigns, and a phx-update="append" flag on the feed. But if the user ever navigates ‘back’ to the feed, after scrolling down and tapping on something, they are dropped back to the very beginning of the feed, losing all of their scroll history.

This is due to the way the state is reset when mount is called after the websocket reconnection, e.g. from this example`:

  def mount(_params, _session, socket) do
     |> assign(page: 1, per_page: 10)
     |> fetch(), temporary_assigns: [users: []]}

The user drops back to “page 1” whenever mount is called, even if that is just when the websocket reconnects after a ‘back’ navigation.

I tried moving the state (the page variable) to the client, so that the socket’s assigns doesn’t have to worry about it, but unfortunately LiveView still clears the container that’s previously been populated from the temporary_assigns variable, after the websocket reconnect.

Is that a bug, or is there some other way to tell LiveView to leave that HTML section in-place so that it can be appended to later once the user scrolls further?

What do you do with the page param? Is the url updated with it?

Yes, I tried adding it to the URL as a GET param, and then using that to re-create state server-side for just that page on mount. But then the user still sees a different scroll position on the page than they had before they navigated away.

So my next attempt was to rather store the page variable client-side with sessionStorage, trying not to touch the state on mount at all. But I haven’t been able to get LiveView to leave the existing content inside the phx-update=“append” container alone on mount.

It looks like the browser renders the feed with the correct scroll position on “back”, but then LiveView clears it out on ‘mount’ after the websocket connects.

You could also store the scroll offset somewhere on client side and restore it after render.

Say you are on /items the you scroll some pages, the url updates to /items?page=3. Then you decide to click on an item link. Before you do anything, a hook updates the location hash:

window.location.hash = '#offset-12345';

12345 being the current scroll offset. So the url becomes /items?page=3#offset-12345

When going back, you need read the location hash back, and use it to scroll with JS.