Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why does using handle_call mean the calling LiveView will be updated twice? #84

Closed
tlietz opened this issue Jul 30, 2022 · 3 comments
Closed
Assignees
Labels
discuss help wanted Extra attention is needed question Further information is requested technical user-feedback

Comments

@tlietz
Copy link

tlietz commented Jul 30, 2022

In the Moving State Out of LiveViews section, there is a note stating: We could have used asyncronous handle_cast functions and relied on the PubSub to update us. Using handle_call means the calling LiveView will be updated twice, but it doesn't really matter at this scale.

Why will the LiveView be updated twice?

I put an IO.inspect() in the render function of LiveViewCounterWeb.Counter liveview to verify that it only gets called once. However, I may be misunderstanding something:

defmodule LiveViewCounterWeb.Counter do

...

def render(assigns) do
    IO.inspect(binding(), label: "RENDER")

    ~L"""
    <div>
      <h1>The count is: <%= @val %></h1>
      <button phx-click="dec">-</button>
      <button phx-click="inc">+</button>
    </div>
    """
  end

...

end

Upon incrementing the counter once, the following is printed:

RENDER: [
  assigns: %{
    __changed__: %{val: true},
    flash: %{},
    live_action: nil,
    socket: #Phoenix.LiveView.Socket<
      assigns: #Phoenix.LiveView.Socket.AssignsNotInSocket<>,
      endpoint: LiveViewCounterWeb.Endpoint,
      id: "phx-FwamoEC_wHbX5AHk",
      parent_pid: nil,
      root_pid: #PID<0.529.0>,
      router: LiveViewCounterWeb.Router,
      transport_pid: #PID<0.523.0>,
      view: LiveViewCounterWeb.Counter,
      ...
    >,
    val: 0
  }
]
@nelsonic nelsonic added help wanted Extra attention is needed question Further information is requested discuss user-feedback technical labels Jul 31, 2022
@nelsonic
Copy link
Member

@tlietz thank you for opening this issue/question. 👍
on re-reading the section, agree that we should remove it. ✂️

@SimonLab SimonLab added this to To do in Simon's list via automation Aug 1, 2022
@SimonLab SimonLab moved this from To do to In progress in Simon's list Aug 2, 2022
@SimonLab
Copy link
Member

SimonLab commented Aug 2, 2022

Currently the counter value is indeed updated twice.

The first time when the incr/decr event is trigger by the user:

def handle_event("inc", _, socket) do
{:noreply, assign(socket, :val, Count.incr())}
end
def handle_event("dec", _, socket) do
{:noreply, assign(socket, :val, Count.decr())}
end

We see that the socket is assigned the new counter value. This will update automatically the value on the client:

Any time a stateful view changes or updates its socket assigns, it is automatically re-rendered and the updates are pushed to the client.

see https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#module-life-cycle

Then because the Count/incr or Count.decr function is called to update the value, PubSub will send another event to all the connected clients, included the user who clicked on the inc/decr button:

defp make_change(count, change) do
new_count = count + change
PubSub.broadcast(LiveViewCounter.PubSub, topic(), {:count, new_count})
{:reply, new_count, new_count}
end

The :count event is handle with:

def handle_info({:count, count}, socket) do
{:noreply, assign(socket, val: count)}
end

Which assigns again the counter value and update again the client.

So this is way the LiveView value is updated twice. However I don't think it is directly a consequences of using handle_call (vs handle_cast) in the GenServer count. If we still assign a value to the socket even if we use handle_cast the client will still be updated twice.

You can test this by removing the assign in the handle function, you will see the counter is still updated because of PubSub:

  def handle_event("inc", _, socket) do
    Count.incr()
    {:noreply, socket}
  end

  def handle_event("dec", _, socket) do
    Count.decr()
    {:noreply, socket}
  end

Concerning the IO.inspect display the values only once, this is normal. LiveView will only call/render the view once then it will update only the html nodes when the socket is updated but the render function will not be called again.

To conclude maybe we can still remove this section in the Readme as I think it creates more confusions.

I hope this makes sense, let me know if you have any other questions @tlietz and thanks for opening this issue.

@SimonLab SimonLab moved this from In progress to Awaiting Feedback/Review in Simon's list Aug 2, 2022
SimonLab added a commit that referenced this issue Aug 2, 2022
Remove section in Readme, Update dependencies and config

see: #84
@SimonLab SimonLab mentioned this issue Aug 2, 2022
SimonLab added a commit that referenced this issue Aug 2, 2022
Remove section in Readme, Update dependencies and config

see: #84
SimonLab added a commit that referenced this issue Aug 2, 2022
Remove section in Readme, Update dependencies and config

see: #84
@SimonLab
Copy link
Member

SimonLab commented Aug 3, 2022

PR removing the section in Readme, merged #85
Don't hesitate to reopen this issue if my explanation is unclear #84 (comment)

@SimonLab SimonLab closed this as completed Aug 3, 2022
@SimonLab SimonLab moved this from Awaiting Feedback/Review to Done in Simon's list Aug 3, 2022
@SimonLab SimonLab removed their assignment Aug 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discuss help wanted Extra attention is needed question Further information is requested technical user-feedback
Projects
Development

No branches or pull requests

3 participants