You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browsers you attempted to reproduce this bug on (the more the merrier):
Firefox and Chrome
Does the problem persist after removing "assets/node_modules" and trying again? Yes/no:
N/A
Actual behavior
Ultimately, this is issue is related to our code. I am reporting it in case it's indicative of something deeper.
We have two liveviews, A and B.
These two liveviews share a layout that has its own (nested) sticky liveview that has a stream. It's a chat client.
Liveview B also has a stream (an audit log), but we accidentally misconfigured it by declaring the stream twice, the second time using the reset: true option.
When navigating from A to B, the nested sticky liveview in the shared layout gets reset on the client, i.e. all the chat messages are removed from the DOM.
A single file app reproducing the issue follows:
Application.put_env(:sample,Example.Endpoint,http: [ip: {127,0,0,1},port: 5001],server: true,live_view: [signing_salt: "aaaaaaaa"],secret_key_base: String.duplicate("a",64))Mix.install([{:plug_cowboy,"~> 2.5"},{:jason,"~> 1.0"},{:phoenix,"~> 1.7.0"},{:phoenix_live_view,"~> 1.0.4"}])defmoduleExample.ErrorViewdodefrender(template,_),do: Phoenix.Controller.status_message_from_template(template)enddefmoduleExample.HomeLivedousePhoenix.LiveView,layout: {__MODULE__,:live}usePhoenix.VerifiedRoutes,endpoint: Example.Endpoint,router: Example.RouteraliasPhoenix.LiveView.JS### This LV does nothing but render its layout which includes a sticky LiveViewdefmount(_params,_session,socket)do{:ok,socket}enddefpphx_vsn,do: Application.spec(:phoenix,:vsn)defplv_vsn,do: Application.spec(:phoenix_live_view,:vsn)defrender("live.html",assigns)do~H"""<scriptsrc={"https://cdn.jsdelivr.net/npm/phoenix@#{phx_vsn()}/priv/static/phoenix.min.js"}></script><scriptsrc={"https://cdn.jsdelivr.net/npm/phoenix_live_view@#{lv_vsn()}/priv/static/phoenix_live_view.min.js"}></script><script> let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket) liveSocket.connect()</script><h4>A layout containing a (nested) sticky LiveView with a stream.</h4><%=live_render(@socket,Example.NestedLive,id: "sticky",sticky: true) %><hr><%=@inner_content %><hr>"""enddefrender(assigns)do~H"""<h3>A LiveView that does nothing but render it's layout.</h3><.linknavigate={~p"/away"}>Go to a different LV with a (funcky) stream</.link>"""endenddefmoduleExample.AwayLivedousePhoenix.LiveView,layout: {Example.HomeLive,:live}usePhoenix.VerifiedRoutes,endpoint: Example.Endpoint,router: Example.RouteraliasPhoenix.LiveView.JSdefmount(_params,_session,socket)dosocket=socket|>stream(:messages,[])|>stream(:messages,[msg(4)],reset: true)# <--- This is the root cause{:ok,socket}enddefrender(assigns)do~H"""<h3>A liveview with a stream configured twice</h3><h4>This causes the nested liveview in the layout above to be reset by the client.</h4><.linknavigate={~p"/"}>Go back to (the now borked) LV without a stream</.link><h1>Normal Stream</h1><divid="msgs-normal"phx-update="stream"><div:for={{dom_id,msg}<-@streams.messages}id={dom_id}><div><%=msg.msg %></div></div></div>"""enddefpmsg(num)do%{id: num,msg: num}endenddefmoduleExample.NestedLivedousePhoenix.LiveView#, layout: {__MODULE__, :live}defmount(_params,_session,socket)do{:ok,stream(socket,:messages,[msg(1),msg(2),msg(3)])}enddefrender(assigns)do~H"""<divid="msgs-sticky"phx-update="stream"><div:for={{dom_id,msg}<-@streams.messages}id={dom_id}><div><%=msg.msg %></div></div></div>"""enddefpmsg(num)do%{id: num,msg: num}endenddefmoduleExample.RouterdousePhoenix.RouterimportPhoenix.LiveView.Routerpipeline:browserdoplug(:accepts,["html"])endscope"/",Exampledopipe_through(:browser)live_session:defaultdolive("/",HomeLive,:index)live("/away",AwayLive,:index)endendenddefmoduleExample.EndpointdousePhoenix.Endpoint,otp_app: :samplesocket("/live",Phoenix.LiveView.Socket)plug(Example.Router)end{:ok,_}=Supervisor.start_link([Example.Endpoint],strategy: :one_for_one)Process.sleep(:infinity)
Expected behavior
As noted, this is my bug. By removing the second stream initialization the problem goes away. However, I think that it should not have been evident in the first place. That is, stream A should not be interfering with stream B.
The text was updated successfully, but these errors were encountered:
Fixes#3681.
Child LiveViews would use the same data-phx-stream-ref, so it could
happen that they were cleared unexpectedly because we did not always
check the owner of the stream element when pruning.
There was another issue at play though: because we used assign_new to
set the streams, nested LiveViews would copy a parent's streams, instead
of creating a fresh one. This would lead to mixed up streams in the dead
render.
Environment
Actual behavior
Ultimately, this is issue is related to our code. I am reporting it in case it's indicative of something deeper.
reset: true
option.A single file app reproducing the issue follows:
Expected behavior
As noted, this is my bug. By removing the second stream initialization the problem goes away. However, I think that it should not have been evident in the first place. That is, stream A should not be interfering with stream B.
The text was updated successfully, but these errors were encountered: