How to correctly update child and manage state with LiveView?

I have a list of structs, and in my LiveView, I pass each struct to a LiveComponent. The LiveComponent manages the logic for updating the struct, but I can’t figure out how to update the overall state (i.e. the list of structs) in the LiveView given one updated struct.

The LiveView docs on LiveView as the source of truth has the exact example of this situation. I’m stuck at the point where

def handle_info({:updated_card, card}, socket) do
    # update the list of cards in the socket 
    # ^^^^here: how do you actually update the list of cards given an updated card?
    {:noreply, updated_socket}
  end

Any help is very appreciated!

Hello and welcome,

There is not much code to answer your question, You could show the main liveview mount…

Anyway, I am making the assumption You have a cards assignement, this would be.

def handle_info({:updated_card, card}, socket) do
  {
    :noreply,
    assign(socket, cards: [card | socket.assigns.cards])
  }
end

EDIT Oh… it is an update, not a create… You should replace in the list the card with id == updated card id.

Something like

Enum.map(socket.assigns.cards, fn c when c.id == card.id -> card; c -> c end)
3 Likes

No need to map manually as LiveView will diff the collection automatically. I have something like this (it works for both scenarios when it’s an existing item that has been updated or when it’s a completely new item):

{:noreply, update(socket, :cards, fn cards -> [card | cards] end)}

Later edit: using the above together with temporary assigns (:cards assign would be a temporary assign).

2 Likes

I learn everyday something new :slight_smile:

1 Like

LiveView will not deduplicate the assign itself, the mapping is definitely necessary. You may be getting the behaviour you want accidentally by having some live component with an ID that gets clobbered, but if you are pushing updated values into that list it will grow indefinitely.

2 Likes

I should have specified I’m using this together with temporary assigns and phx-update in prepend/append state.

Thank you all for your help!

I think @benwilson512 is right. AFAIK using temporary assigns with phx-update only updates the UI of the corresponding card that got updated and throws the actual structs away. This would be useful if my list of structs were persisted and I was updating one of the structs, but this is not my case.

I’m have a list of non-persisted structs and after a user is done updating each one of them, I persist them using Multi. So @kokolegorille’s answer of just mapping over them is likely the best (maybe only?) solution.

Guess the object-oriented parts of me didn’t want to accept that I have to map over the entire list to update just one struct :man_shrugging: