Phoenix Liveview stream reset: true not actually resetting the list in the DOM

I’m using the latest phoenix_live_view 0.20.3

I’m seeing the same issue with offset/limit for filtering records using Liveview streams:

<div
  class="flex flex-col gap-6"
  id="jobs"
  phx-update="stream"
  phx-viewport-top={@page > 1 && "prev-page"}
  phx-viewport-bottom={!@end_of_timeline? && "next-page"}
  phx-page-loading
  class={[
    if(@end_of_timeline?, do: "pb-10", else: "pb-[calc(200vh)]"),
    if(@page == 1, do: "pt-10", else: "pt-[calc(200vh)]")
  ]}
>
  <.live_component
    :for={{_, job} <- @streams.jobs}
    module={JobListItem}
    id={"list-item-job-#{job.id}"}
    job={job}
  />

  <div :if={@end_of_timeline?} class="mt-5 text-[50px] text-center">
    🎉 Llegaste al final de la lista! 🎉
  </div>
</div>

How I handle filter opts form:

def handle_params(params, uri, socket) do
  url = URI.parse(uri) |> URI.to_string()

  filter_opts = %{
    categories: params["categories"] || [],
    cities: params["cities"] || [],
    search: params["search"] || ""
  }

  socket =
    socket
    |> assign(:share_url, url)
    |> assign(:meta_url, url)
    |> assign(:filters, filter_opts)
    |> assign(page: 1, per_page: 20)

  {:noreply, paginate_jobs(socket, socket.assigns.page)}
end

defp paginate_jobs(socket, new_page) when new_page >= 1 do
  %{per_page: per_page, page: cur_page} = socket.assigns

  jobs =
    Jobs.search_jobs(
      filter_opts: socket.assigns.filters,
      offset: (new_page - 1) * per_page,
      limit: per_page
    )

  {jobs, at, limit} =
    if new_page >= cur_page do
      {jobs, -1, per_page * 3 * -1}
    else
      {Enum.reverse(jobs), 0, per_page * 3}
    end

  case jobs do
    [] ->
      assign(socket, end_of_timeline?: at == -1)

    [_ | _] = jobs ->
      socket =
        socket
        |> assign(end_of_timeline?: false)
        |> assign(:page, new_page)

      if cur_page == 1 do
        IO.inspect("RESETTING")
        stream(socket, :jobs, jobs, reset: true)
      else
        IO.inspect("NOT RESETTING")
        stream(socket, :jobs, jobs, at: at, limit: limit)
      end
  end
end

This still happens on phoenix_live_view 0.20.3. I wonder if it’s because I go push_patch (when filter options form changes) -> handle_params (to apply new filter opts) -> I call {:noreply, paginate_jobs(socket, socket.assigns.page)} at the end of handle_params? :thinking:

In my liveview: stream(socket, :jobs, jobs, reset: true) this doesn’t empty out the existing results from the previous filters in the DOM, but appends the new correct items to the bottom of the list.

Changing this:

{:noreply, push_patch(socket, to: ~p"/search?#{filter_opts}")}

To this:

{:noreply, push_navigate(socket, to: ~p"/search?#{filter_opts}")}

Fixed my issue.

Is this the right approach?

push_patch should work fine and I think switching to push_navigate is actually just masking the problem by mounting a new LV process.

Can you clarify what you’re having troubles with here? Your question seems to indicate that reset: true isn’t working but your code snippet says it is:

IO.inspect("RESETTING")
stream(socket, :jobs, jobs, reset: true)

IO.inspect("NOT RESETTING")
stream(socket, :jobs, jobs, at: at, limit: limit)

There is still a bit of discussion going on about this on github. I still get odd behaviour which I outlined here and have to revert back to 1.19 for the time-being. To be clear, I’m using reset: true.

This PR fixed my problem.

Also I have to make sure my live component had the id of the stream iterator.

LV stream still has a bug with :reset and unfortunately it was not fixed in 0.20.3. It just manifests differently.

2 Likes

Please try using the latest version from the main branch ({:phoenix_live_view, github: "phoenixframework/phoenix_live_view", branch: "main", override: true}). It includes refactor stream reset reordering (fixes #2994) by SteffenDE · Pull Request #2996 · phoenixframework/phoenix_live_view · GitHub which should hopefully fix stream reset once and for all :smile:

4 Likes

You did it, main works! You are my hero until further notice!* Thank you for the work you put into this!

(*) I may not actually give notice :upside_down_face:

2 Likes

Yup, the demo app replicating the bug seems to be working properly now.

One of these days, I’m gonna try it with the real app (by removing the extra stream( socket, name, [], reset: true) cycle just to insert items anew in the next.

Thanks!

1 Like