From b252dc0620b64c6875d3a9413c89315b1fa7de88 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 6 Nov 2025 12:48:11 -0600 Subject: [PATCH] Fix adding nodes to nested fragments (#4929) * Fix adding nodes to nested fragments * add note about test --- packages/core/src/diff/node.rs | 6 +- packages/core/tests/diff_unkeyed_list.rs | 80 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/packages/core/src/diff/node.rs b/packages/core/src/diff/node.rs index 6c8e7a26e..5bac17d93 100644 --- a/packages/core/src/diff/node.rs +++ b/packages/core/src/diff/node.rs @@ -188,10 +188,10 @@ impl VNode { Some((idx, Placeholder(_) | Text(_))) => { ElementId(dom.get_mounted_dyn_node(mount_id, idx)) } - // The node is a fragment, so we need to find the first element in the fragment + // The node is a fragment, so we need to find the last element in the fragment Some((_, Fragment(children))) => { - let child = children.first().unwrap(); - child.find_first_element(dom) + let child = children.last().unwrap(); + child.find_last_element(dom) } // The node is a component, so we need to find the first element in the component Some((id, Component(_))) => { diff --git a/packages/core/tests/diff_unkeyed_list.rs b/packages/core/tests/diff_unkeyed_list.rs index fb3246118..cb093e697 100644 --- a/packages/core/tests/diff_unkeyed_list.rs +++ b/packages/core/tests/diff_unkeyed_list.rs @@ -509,3 +509,83 @@ fn replace_and_add_items() { ); } } + +// Simplified regression test for https://github.com/DioxusLabs/dioxus/issues/4924 +#[test] +fn nested_unkeyed_lists() { + let mut dom = VirtualDom::new(|| { + let content = if generation() % 2 == 0 { + vec!["5\n6"] + } else { + vec!["1\n2", "3\n4"] + }; + + rsx! { + for one in &content { + for line in one.lines() { + p { "{line}" } + } + } + } + }); + + // The list starts with one placeholder + { + let edits = dom.rebuild_to_vec(); + assert_eq!( + edits.edits, + [ + // load the p tag template + LoadTemplate { index: 0, id: ElementId(1) }, + // Create the first text node + CreateTextNode { value: "5".into(), id: ElementId(2) }, + // Replace the placeholder inside the p tag with the text node + ReplacePlaceholder { path: &[0], m: 1 }, + // load the p tag template + LoadTemplate { index: 0, id: ElementId(3) }, + // Create the second text node + CreateTextNode { value: "6".into(), id: ElementId(4) }, + // Replace the placeholder inside the p tag with the text node + ReplacePlaceholder { path: &[0], m: 1 }, + // Add the text nodes to the root node + AppendChildren { id: ElementId(0), m: 2 } + ] + ); + } + + // DOM state: + //
 # Id 1 for if statement
+    // 

# Id 2 + // "5" # Id 3 + //

# Id 4 + // "6" # Id 5 + // + // The diffing engine should add two new elements to the end and modify the first two elements in place + { + dom.mark_dirty(ScopeId::APP); + let edits = dom.render_immediate_to_vec(); + assert_eq!( + edits.edits, + [ + // load the p tag template + LoadTemplate { index: 0, id: ElementId(5) }, + // Create the third text node + CreateTextNode { value: "3".into(), id: ElementId(6) }, + // Replace the placeholder inside the p tag with the text node + ReplacePlaceholder { path: &[0], m: 1 }, + // load the p tag template + LoadTemplate { index: 0, id: ElementId(7) }, + // Create the fourth text node + CreateTextNode { value: "4".into(), id: ElementId(8) }, + // Replace the placeholder inside the p tag with the text node + ReplacePlaceholder { path: &[0], m: 1 }, + // Insert the text nodes after the second p tag + InsertAfter { id: ElementId(3), m: 2 }, + // Set the first text node to "1" + SetText { value: "1".into(), id: ElementId(2) }, + // Set the second text node to "2" + SetText { value: "2".into(), id: ElementId(4) } + ] + ); + } +}