Closed davydog187 closed 3 months ago
Maybe we'll need to go a little deeper here, because simply stating that it maintains change tracking may lead to wrong assumptions. While it is true that using assign/3
etc. in a function maintains change tracking, it only maintains it in the sense that nothing breaks. But the items that are assigned are always considered to be changed and therefore always sent over the wire. So if you do something like
def my_component(assigns) do
assigns = assign(assigns, :foo, assigns.a + assigns.b)
~H"""
<%= @foo %>
"""
end
This will always send the diff for foo
, even if a
and b
did not change. I feel like this was the intention behind the line
Instead explicitly precompute the assign outside of render
which was already changed in https://github.com/phoenixframework/phoenix_live_view/commit/cea2ab233cc34ce7a3b2659ebf7423909e691347 because people misunderstood it that assign/3
is not possible in render
, but maybe it's kind of worse now in that regard.
In function components the "assign at the top of the function" pattern is often used from what I've seen. I'm not sure if I'd go that far, but one could currently call it an anti pattern for diff size over the wire.
Opinions? @chrismccord @josevalim
(Maybe an assign/3
macro could be more intelligent in such cases?)
@SteffenDE you are correct. The point I wanted to clarify is that the surrounding template will not be resent, even if the variable @foo
has.
Consider the following LiveView
defmodule MyAppWeb.TestLive do
use MyAppWeb, :live_view
def mount(_, _, socket) do
if connected?(socket) do
send(self(), :tick)
end
{:ok, socket |> assign_random()}
end
def handle_info(:tick, socket) do
Process.send_after(self(), :tick, :timer.seconds(1))
{:noreply, assign_random(socket)}
end
defp assign_random(socket) do
assign(socket, x: Enum.random(1..1000), y: Enum.random(1..1000))
end
def show_math(assigns) do
assigns = assign(assigns, :sum, assigns.x + assigns.y)
~H"""
<div class="border p-4">
<span class="text-bold"><%= @x %></span>
<span class="text-bold">+</span>
<span class="text-bold"><%= @y %></span>
<span class="text-bold"><%= @sum %></span>
</div>
"""
end
def render(assigns) do
~H"""
<h1>Let's do math!</h1>
<.show_math x={@x} y={@y} />
"""
end
end
After the initial render, only the changes to @x
, @y
and @sum
are sent over the wire, which was not my mental model for using assigns/3
. The point I want to clarify for readers is that you don't need to worry about the static parts of the template changing.
This might mean that change tracking
might need a clearer definition, with possibly more examples of how it degrades in certain scenarios. I am not alone in being confused about the behavior here
I pushed a version as a comment, let me know what you think.
I have an idea to clarify further, will push something later. Thanks @josevalim and @SteffenDE
@josevalim take another look. The main thing I think that the docs were missing is exactly how the rendering degrades in certain failure scenarios.
Particularly, by only using a single variable @sum
, it was hard to illustrate how using variables leads to bad templates. I hope this is a good tradeoff between clarity and understanding
:green_heart: :blue_heart: :purple_heart: :yellow_heart: :heart:
I was under the impression that using
assign/3
and friends inside of a function would break change tracking, which is not the case.A quick note to clarify this behavior