Skip to content

Commit 5d53ba4

Browse files
authored
raise custom error for duplicate ID when using keyed comprehensions (#3857)
* raise custom error for duplicate ID when using keyed comprehensions * add test
1 parent 946feba commit 5d53ba4

File tree

4 files changed

+33
-8
lines changed

4 files changed

+33
-8
lines changed

lib/phoenix_component.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -850,10 +850,13 @@ defmodule Phoenix.Component do
850850
apply to comprehensions defined with `:key`:
851851
852852
1. A `:key` can only be defined on regular HTML tags, not on components or slots.
853-
2. The diff over the wire is optimized to only send changes for each item,
853+
2. The `key` must be globally unique. LiveView prefixes the given value with the module,
854+
line and file where the `:key` is defined, but there can still be situations where
855+
conflicts occur.
856+
3. The diff over the wire is optimized to only send changes for each item,
854857
but it will always include a list of component IDs (integers) specifying
855858
the overall order of items.
856-
3. Removing an entry involves separate round-trips with the client to confirm
859+
4. Removing an entry involves separate round-trips with the client to confirm
857860
the component removal.
858861
859862
We recommend to use `:key`ed comprehensions only if you already determined that you need

lib/phoenix_live_view/diff.ex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,15 @@ defmodule Phoenix.LiveView.Diff do
645645
Enum.reduce(entries, {[], [], components, seen_ids}, fn
646646
{cid, id, new?, new_assigns}, {assigns_sockets, metadata, components, seen_ids} ->
647647
if Map.has_key?(seen_ids, [component | id]) do
648-
raise "found duplicate ID #{inspect(id)} " <>
649-
"for component #{inspect(component)} when rendering template"
648+
case id do
649+
{:keyed_comprehension, module, line, column, key} ->
650+
raise "found duplicate key #{inspect(key)} " <>
651+
"for keyed comprehension in module #{inspect(module)} at line #{line} column #{column}"
652+
653+
_ ->
654+
raise "found duplicate ID #{inspect(id)} " <>
655+
"for component #{inspect(component)} when rendering template"
656+
end
650657
end
651658

652659
{socket, components, prints} =

lib/phoenix_live_view/tag_engine.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,8 +1204,8 @@ defmodule Phoenix.LiveView.TagEngine do
12041204
ast =
12051205
quote do
12061206
Phoenix.LiveView.TagEngine.keyed_comprehension(
1207-
{unquote(state.caller.module), unquote(tag_meta.line), unquote(tag_meta.column),
1208-
unquote(key_expr)},
1207+
{:keyed_comprehension, unquote(state.caller.module), unquote(tag_meta.line),
1208+
unquote(tag_meta.column), unquote(key_expr)},
12091209
%{unquote_splicing(Map.to_list(variables))},
12101210
do: unquote(invoke_subengine(state, :handle_end, [[meta: [root: true]]]))
12111211
)

test/phoenix_live_view/diff_test.exs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,7 +1783,8 @@ defmodule Phoenix.LiveView.DiffTest do
17831783

17841784
assert {%{
17851785
1 =>
1786-
{Phoenix.LiveView.KeyedComprehension, {Phoenix.LiveView.DiffTest, _, _, 1},
1786+
{Phoenix.LiveView.KeyedComprehension,
1787+
{:keyed_comprehension, Phoenix.LiveView.DiffTest, _, _, 1},
17871788
%{
17881789
id: 1,
17891790
name: "First",
@@ -1792,7 +1793,8 @@ defmodule Phoenix.LiveView.DiffTest do
17921793
myself: %Phoenix.LiveComponent.CID{cid: 1}
17931794
}, %{}, _},
17941795
2 =>
1795-
{Phoenix.LiveView.KeyedComprehension, {Phoenix.LiveView.DiffTest, _, _, 2},
1796+
{Phoenix.LiveView.KeyedComprehension,
1797+
{:keyed_comprehension, Phoenix.LiveView.DiffTest, _, _, 2},
17961798
%{
17971799
id: 2,
17981800
name: "Second",
@@ -1911,5 +1913,18 @@ defmodule Phoenix.LiveView.DiffTest do
19111913
:c => %{1 => %{1 => "Updated", 2 => "Updated"}}
19121914
}
19131915
end
1916+
1917+
test "raises on duplicate key" do
1918+
assigns = %{socket: %Socket{}}
1919+
1920+
rendered = ~H"""
1921+
<.keyed_comprehension_with_pattern items={[%{id: 1, name: "One"}]} />
1922+
<.keyed_comprehension_with_pattern items={[%{id: 1, name: "One"}]} />
1923+
"""
1924+
1925+
assert_raise RuntimeError,
1926+
~r/found duplicate key 1 for keyed comprehension in module Phoenix.LiveView.DiffTest/,
1927+
fn -> render(rendered) end
1928+
end
19141929
end
19151930
end

0 commit comments

Comments
 (0)