Using stream in livew views with additional data


  • LiveView version 0.18.16
  • Elixir .1.14.1

I’m trying to figure out how to make the built-in table component update when some async data comes:

   <%= inspect(@job_remote_data) %>
<.table id="jobs" rows={}>
  <:col :let={{_id, job}} label="Queue Size">
      <%= inspect(@job_remote_data) %>

The problem is, the data in <:col> gets out of sync with the live-view state. I’m not sure if that’s intended or a bug, but I want to forcefully re-render my column that’s showing @job_remote_data

%{"my_instance" => %{queues: %{in: 0, out: 0}}}

However, the same inspect lines a few lines down shows:


The data is being fetched like so:

def handle_info(:fetch_data, socket) do
    result =
      |> node -> {node, Remote.get_queues(node)} end)
      |> {node, {:ok, in_size, out_size}} ->
        {Remote.short_name(node), %{queues: %{in: in_size, out: out_size}}}
      |> Enum.into(%{})

    IO.puts("RESULT IS: ")
    {:noreply, socket |> assign(:job_remote_data, result)}

The results get fetched, the view re-renders, BUT the column that has a reference to this state remains stale, still shows the initial state of %{}

I was able to solve it by making a work-around, by using live-component here, but isn’t there a cleaner way to make this column update and be in sync?

I’m new to LiveView, coming from React, so I might be thinking of the whole “rendering” process in a biased way, and I don’t seem to understand how it works.




Though it may be surprising, I think this is intended behavior and not a bug!

LiveView streams are a very new feature to “stream” resources to the client side and freeing up memory on the server side which now only has to keep track of DOM ids.

To accomplish this, the parent DOM container of streamed resources must include a phx-update="stream" attribute and the aformentioned unique DOM id. That’s why the table function/component within the generated core_components.ex file returns a HEEx template containing phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}. This then lets LiveView know to ignore streamed sections of the HEEx template when re-rendering/updating until they are explicitly updated/re-rendered via the stream_insert and stream_delete(_by_dom_id) functions that LiveView provides.

So one possible approach is to re-stream_insert your jobs instead of updating the :job_remote_data assign in your handle_info(:fetch_data, socket) callback function. Another approach would be to use Phoenix.LiveView.push_event in the :fetch_data event handler in conjunction with a client side JS Hook to update that queue size column.

Taking a step back, it’s mixing traditional assigns within/inside of the new streams that leads to this unexpected stale client side state.