Liveview re-renders all assigns inside `for` loop, not only the ones that changed

When inspecting what’s being passed from the server to the client I realise that more data is sent than what I was expecting for assigns inside a for loop.
I have this code:

<%= for i <- 1..3 do %>
      <div class="valueWrap <%= i %>">
           <div class="value <%= i %>">
                <%= Enum.at(@data, i-1 %>
           </div>
           <div class="label <%= i %>">
              <%= Enum.at(@labels, i-1) %>
           </div>
       </div>
<%end%>

Where data is a dynamic list like [10, 200, 95]
and labels: ["New orders", "Sales amount", "Satisfaction"]
The only assigns that changes is data. i and id are dynamically built but, for example, labels never changes.
Why are all this assigns sent every time data changes? According to the docs Liveview can track diffs inside a for loop and I was expecting diff to figure it out and only send the updated data.

This is likely caused by the Enum.with_index around the @data.

Thanks, but no, I tried with a range and it’s the same.
I will edit the code to simplify it.

It does diff tracking in the sense that it won’t re-render the for loop unless @data or @labels changes. If they do change, then it sends the whole block.

If you want change tracking at the individual item level, you can either use a stateful LiveComponent, or temporary assigns and phx-update. See this blog post for more details on the tradeoff: Optimising data-over-the-wire in Phoenix LiveView – The Pug Automatic.

1 Like

Anyway, it would be wonderfull it this issue could be solved natively by Liveview :slight_smile:

This is difficult to do. Enum.with_index could do anything with the data, therefore liveview cannot optimize the resulting code in any way – change tracking is no longer possible. This is documented here: Assigns and LiveEEx templates — Phoenix LiveView v0.15.4

The solution would be to store the list with indexes applied already instead of adding the index in the template.