How to update the state in socket without triggering a diff?

I’ve got a basic counter in LiveView where you click on a button and the counter increments by one. I’ve got a GenServer that broadcasts the counter value to all the processes(?) —that message is broadcast to the processes first, right? Correct me please, if I’m wrong— in every 5 seconds or so, and then I handle this info to push an event to the client. On the client side I basically check whether the counter value on the page is ahead of the value that was broadcast, if so I basically don’t do anything. Otherwise I set the new counter value that was broadcast.

My problem is that, since I am using the state in the socket I also need to update the value in socket.assigns, otherwise after the broadcast even though the counter value on the page changes the actual value of the counter in socket.assigns doesn’t change. It’ll be clear with the code I think:

# counter_live.ex

  # This is where I handle button clicks, to increment the counter value
  @impl true
  def handle_event("inc", _session, socket) do
    Task.start_link(fn -> Counter.increment() end)

    {:noreply,
      socket
      |> update(:counter_value, &(&1 + 1))
    }
  end

  # This is where I handle the broadcast
  @impl true
  def handle_info(%{event: "update_counter_value", payload: payload}, socket) do
    {:noreply,
      socket
      |> assign(payload)
      |> push_event("update_counter_value", payload)
    }
  end

Respective leex file:

<!-- counter_live.html.leex -->
<section>
  <div id="counter">
    <span id="counter-value" class="counter-value" phx-hook="counterUpdated"><%= @counter_value %></span>
    <div><%= link "Increment", to: "#", phx_click: "inc", class: "button" %></div>
  </div>
</section>

As you see above, in handle_info function if I don’t use assign since the value in the socket doesn’t change on the next button click the value will be back to the old value in socket.assigns which you can see below.

If I use assign and push_event both at the same time then there’s a glitch in the UI where you can basically see the value going back to the old one and then JS updates it back to the broadcast value, which is annoying to see. I tried to record it which you can see below hopefully.

How can I handle this situation better? Basically I need to update socket.assigns without triggering a diff pushed to the client (so only JS handles the UI update) but that’s not possible I’m told. :smiley:

It might be helpful if you showed the markup, are you using the assign in the HTML?

Sorry I forgot to include that. You can see the HTML above, I’ve updated the question. Yes, I use @counter_value while rendering the page for the first time.

Well it’s part of your view, which means it used any time it is rendered, not just the first time. And the re-render is supposed to be largely derivative from the assigns. If you don’t want Phoenix to be able to update that inner content though you can add phx-update="ignore" to the span

2 Likes

Yes, I think I could use phx-update="ignore" and then update the UI with the hooks but then I am not sure whether using LiveView really makes sense in this situation. I was thinking maybe there’s a way to achieve what I wanted to do without ignoring the updates.

You can either keep the state server side (in the assign) or client side (in js data structure), but try not to have it both ways. Server side state (such as your counter) can be rendered in liveView through diffs, so you don’t need to push_event. On the other hand, if you always push_event and render the content client side, you don’t need to keep the counter in assign.