Currently when composing nested LiveComponents child components do not have access to "parent" assigns.
Only when parent assigns are copied into child assigns do children have access and only in update/2, not while mounting.
For me it would be very convenient to be able to read contextual data while mounting a stateful child component.
It would enable me to initialize an independent child state derived from "parent" state once while mounting.
Function render_pending_components/5 in Phoenix.LiveView.Diff could pass contextual data to mount_component/3.
{socket, components} =
case cid_to_component do
%{^cid => {_component, _id, assigns, private, prints}} ->
{configure_socket_for_component(socket, assigns, private, prints), components}
%{} ->
{mount_component(socket, component, %{myself: cid}, cid_to_component),
put_cid(components, component, id, cid)}
end
Then mount_component/3 would become mount_component/4.
defp mount_component(socket, component, assigns, context \\ %{}) do
socket =
configure_socket_for_component(
socket,
assigns,
Map.take(socket.private, [:conn_session]),
new_fingerprints()
)
|> Utils.assign(:flash, %{})
Utils.maybe_call_mount!(socket, component, [context, socket])
end
Only when the client is interested in receiving contextual data would Phoenix.LiveView.Utils format the data and pass it to a mount function.
def maybe_call_mount!(socket, view, args) do
# arity = length(args)
{arity, args} =
case length(args) do
2 -> if function_exported?(view, :mount, 2) do
{2, format_context(args)}
else
{1, tl(args)} # drop context from args
end
n -> {n, args}
end
Possible formatting.
defp format_context([context, socket]) do
formatted =
Enum.reduce(context, %{}, fn {_, {component, id, assigns, _private, _prints}}, acc ->
Map.put(acc, id, {component, assigns})
end)
[formatted, socket]
end
Mount function in LiveComponent.
def mount(context, socket) do
# assign independent child state derived from context
{:ok, socket}
end
I am hoping something like this could be considered.
Cheers,
Ingmar