How to replace LiveView stream with new items? (reset or re-assigning)

I expected that the existing stream to be replaced with new items when stream is assigned again, but it isn’t.
It crashes with ** (ArgumentError) existing hook :messages(stream name) already attached on :after_render.

  @impl true
  def update(assigns, socket) do
    ...

    socket =
      socket
      |> assign(assigns)
      |> assign_messages(new_channel_name)

    {:ok, socket}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div>
      <.h2># <%= @channel_name %></.h2>
      <p>Your ID: <%= @session_id %></p>
      <div id="messages" class="h-96 overflow-y-auto" phx-update="stream" phx-hook="ChatMessages">
        <div :for={{message_id, message} <- @streams.messages} id={message_id} class="mt-4">
          <p>User id: <%= message.user_id %></p>
          <p><%= message.created_at %></p>
          <p><%= message.body %></p>
        </div>
      </div>
      ...
    </div>
    """
  end

  defp assign_messages(socket, channel_name) do
    {:ok, messages} = Chat.list_messages(channel_name)

    socket
    |> stream(:messages, messages)
    ...
  end

You can test it on the page below, by changing channel.

Is it possible to replace stream with new items? (re-assigning)

page: Home · Json Media
code: json_corp/channel.ex at chat · nallwhy/json_corp · GitHub

2 Likes

At the moment, you can stream_insert to update or add or stream_delete to delete individual elements e.g. message within @streams.messages within an existing stream. Emptying/clearing out an existing stream at the @streams.messages level isn’t currently supported, but it is on the roadmap.

2 Likes

Thank you!

FYI, this is now implemented.

https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#stream/4-resetting-a-stream

Resetting a stream

To empty a stream container on the client, you can pass :reset with an empty list:

stream(socket, :songs, [], reset: true)

Or you can replace the entire stream on the client with a new collection:

stream(socket, :songs, new_songs, reset: true)
1 Like

FYI, if you do replace a stream, note that the entire collection in the stream is sent down the wire. For many items, that can be quite large. I generated a new app with the default CRUD UI and generated liveview scafolding for posts with a title, ID. For 1000 items, the WebSocket frame had 1.3MB of data sent down the wire. Consider using infinite scrolling as discussed in the guide here: Bindings — Phoenix LiveView v0.20.14

1 Like