-
Notifications
You must be signed in to change notification settings - Fork 959
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix streams in sticky LV being reset when same ref (#3683)
* fix streams in sticky LV being reset when same ref 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. * fix test on latest elixir * add test
- Loading branch information
Showing
5 changed files
with
139 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
defmodule Phoenix.LiveViewTest.E2E.Issue3681Live do | ||
# https://github.com/phoenixframework/phoenix_live_view/issues/3681 | ||
|
||
use Phoenix.LiveView, layout: {__MODULE__, :live} | ||
|
||
alias Phoenix.LiveView.JS | ||
|
||
def mount(_params, _session, socket) do | ||
{:ok, socket} | ||
end | ||
|
||
def render("live.html", assigns) do | ||
~H""" | ||
{apply(Phoenix.LiveViewTest.E2E.Layout, :render, [ | ||
"live.html", | ||
Map.put(assigns, :inner_content, []) | ||
])} | ||
{live_render( | ||
@socket, | ||
Phoenix.LiveViewTest.E2E.Issue3681.StickyLive, | ||
id: "sticky", | ||
sticky: true | ||
)} | ||
<hr /> | ||
{@inner_content} | ||
<hr /> | ||
""" | ||
end | ||
|
||
def render(assigns) do | ||
~H""" | ||
<h3>A LiveView that does nothing but render it's layout.</h3> | ||
<.link navigate="/issues/3681/away">Go to a different LV with a (funcky) stream</.link> | ||
""" | ||
end | ||
end | ||
|
||
defmodule Phoenix.LiveViewTest.E2E.Issue3681.AwayLive do | ||
use Phoenix.LiveView, layout: {Phoenix.LiveViewTest.E2E.Issue3681Live, :live} | ||
|
||
alias Phoenix.LiveView.JS | ||
|
||
def mount(_params, _session, socket) do | ||
socket = | ||
socket | ||
|> stream(:messages, []) | ||
# <--- This is the root cause | ||
|> stream(:messages, [msg(4)], reset: true) | ||
|
||
{:ok, socket} | ||
end | ||
|
||
def render(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> | ||
<.link navigate="/issues/3681">Go back to (the now borked) LV without a stream</.link> | ||
<h1>Normal Stream</h1> | ||
<div id="msgs-normal" phx-update="stream"> | ||
<div :for={{dom_id, msg} <- @streams.messages} id={dom_id}> | ||
<div>{msg.msg}</div> | ||
</div> | ||
</div> | ||
""" | ||
end | ||
|
||
defp msg(num) do | ||
%{id: num, msg: num} | ||
end | ||
end | ||
|
||
defmodule Phoenix.LiveViewTest.E2E.Issue3681.StickyLive do | ||
use Phoenix.LiveView, layout: false | ||
|
||
def mount(_params, _session, socket) do | ||
{:ok, stream(socket, :messages, [msg(1), msg(2), msg(3)])} | ||
end | ||
|
||
def render(assigns) do | ||
~H""" | ||
<div id="msgs-sticky" phx-update="stream"> | ||
<div :for={{dom_id, msg} <- @streams.messages} id={dom_id}> | ||
<div>{msg.msg}</div> | ||
</div> | ||
</div> | ||
""" | ||
end | ||
|
||
defp msg(num) do | ||
%{id: num, msg: num} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
const {test, expect} = require("../../test-fixtures") | ||
const {syncLV} = require("../../utils") | ||
|
||
// https://github.com/phoenixframework/phoenix_live_view/issues/3681 | ||
test("streams in nested LiveViews are not reset when they share the same stream ref", async ({page, request}) => { | ||
// this was a separate bug where child LiveViews accidentally shared the parent streams | ||
// check that the initial render does not contain the messages-4 element twice | ||
expect((await (await request.get("/issues/3681/away")).text()).match(/messages-4/g).length).toBe(1) | ||
|
||
await page.goto("/issues/3681") | ||
await syncLV(page) | ||
|
||
await expect(page.locator("#msgs-sticky > div")).toHaveCount(3) | ||
|
||
await page.getByRole("link", {name: "Go to a different LV with a (funcky) stream"}).click() | ||
await syncLV(page) | ||
await expect(page.locator("#msgs-sticky > div")).toHaveCount(3) | ||
|
||
await page.getByRole("link", {name: "Go back to (the now borked) LV without a stream"}).click() | ||
await syncLV(page) | ||
await expect(page.locator("#msgs-sticky > div")).toHaveCount(3) | ||
}) |