From 33f3d40a499b4542ca33134d59095a56ec1f1894 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 30 Jul 2024 22:05:56 +0200 Subject: [PATCH] Fix diffing Option (#2746) --- packages/core/src/diff/mod.rs | 13 ++++++- packages/core/src/diff/node.rs | 44 +++++++++++++++++++++- packages/core/tests/diff_dynamic_node.rs | 47 ++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 packages/core/tests/diff_dynamic_node.rs diff --git a/packages/core/src/diff/mod.rs b/packages/core/src/diff/mod.rs index 6cb4cccca..4ed5e70ab 100644 --- a/packages/core/src/diff/mod.rs +++ b/packages/core/src/diff/mod.rs @@ -44,11 +44,20 @@ impl VirtualDom { ) { let m = self.create_children(to.as_deref_mut(), r, parent); if let Some(to) = to { - to.replace_node_with(placeholder_id, m); - self.reclaim(placeholder_id); + self.replace_placeholder_with_nodes_on_stack(to, placeholder_id, m) } } + fn replace_placeholder_with_nodes_on_stack( + &mut self, + to: &mut impl WriteMutations, + placeholder_id: ElementId, + m: usize, + ) { + to.replace_node_with(placeholder_id, m); + self.reclaim(placeholder_id); + } + fn nodes_to_placeholder( &mut self, mut to: Option<&mut impl WriteMutations>, diff --git a/packages/core/src/diff/node.rs b/packages/core/src/diff/node.rs index bf6d95e65..f68cff1aa 100644 --- a/packages/core/src/diff/node.rs +++ b/packages/core/src/diff/node.rs @@ -84,6 +84,12 @@ impl VNode { self.diff_vtext(to, mount, idx, old, new) } }, + (Text(_), Placeholder(_)) => { + self.replace_text_with_placeholder(to, mount, idx, dom) + }, + (Placeholder(_), Text(new)) => { + self.replace_placeholder_with_text(to, mount, idx, new, dom) + }, (Placeholder(_), Placeholder(_)) => {}, (Fragment(old), Fragment(new)) => dom.diff_non_empty_fragment(to, old, new, Some(self.reference_to_dynamic_node(mount, idx))), (Component(old), Component(new)) => { @@ -100,6 +106,42 @@ impl VNode { }; } + /// Replace a text node with a placeholder node + pub(crate) fn replace_text_with_placeholder( + &self, + to: Option<&mut impl WriteMutations>, + mount: MountId, + idx: usize, + dom: &mut VirtualDom, + ) { + if let Some(to) = to { + // Grab the text element id from the mount and replace it with a new placeholder + let text_id = ElementId(dom.mounts[mount.0].mounted_dynamic_nodes[idx]); + let (id, _) = self.create_dynamic_node_with_path(mount, idx, dom); + to.create_placeholder(id); + to.replace_node_with(text_id, 1); + dom.reclaim(text_id); + } + } + + /// Replace a placeholder node with a text node + pub(crate) fn replace_placeholder_with_text( + &self, + to: Option<&mut impl WriteMutations>, + mount: MountId, + idx: usize, + new: &VText, + dom: &mut VirtualDom, + ) { + if let Some(to) = to { + // Grab the placeholder id from the mount and replace it with a new text node + let placeholder_id = ElementId(dom.mounts[mount.0].mounted_dynamic_nodes[idx]); + let (new_id, _) = self.create_dynamic_node_with_path(mount, idx, dom); + to.create_text_node(&new.value, new_id); + dom.replace_placeholder_with_nodes_on_stack(to, placeholder_id, 1); + } + } + /// Try to get the dynamic node and its index for a root node pub(crate) fn get_dynamic_root_node_and_id( &self, @@ -880,7 +922,7 @@ impl VNode { ) -> usize { let (id, path) = self.create_dynamic_node_with_path(mount, idx, dom); - // If this is a root node, the path is empty and we need to create a new text node + // If this is a root node, the path is empty and we need to create a new placeholder node if path.is_empty() { to.create_placeholder(id); // We create one node on the stack diff --git a/packages/core/tests/diff_dynamic_node.rs b/packages/core/tests/diff_dynamic_node.rs new file mode 100644 index 000000000..11a5aba1f --- /dev/null +++ b/packages/core/tests/diff_dynamic_node.rs @@ -0,0 +1,47 @@ +use dioxus::dioxus_core::{ElementId, Mutation::*}; +use dioxus::prelude::*; +use pretty_assertions::assert_eq; + +#[test] +fn toggle_option_text() { + let mut dom = VirtualDom::new(|| { + let gen = generation(); + let text = if gen % 2 != 0 { Some("hello") } else { None }; + + rsx! { + div { + {text} + } + } + }); + + // load the div and then assign the None as a placeholder + assert_eq!( + dom.rebuild_to_vec().sanitize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, + AssignId { path: &[0], id: ElementId(2,) }, + AppendChildren { id: ElementId(0), m: 1 }, + ] + ); + + // Rendering again should replace the placeholder with an text node + dom.mark_dirty(ScopeId::APP); + assert_eq!( + dom.render_immediate_to_vec().sanitize().edits, + [ + CreateTextNode { value: "hello".to_string(), id: ElementId(3,) }, + ReplaceWith { id: ElementId(2,), m: 1 }, + ] + ); + + // Rendering again should replace the placeholder with an text node + dom.mark_dirty(ScopeId::APP); + assert_eq!( + dom.render_immediate_to_vec().sanitize().edits, + [ + CreatePlaceholder { id: ElementId(2,) }, + ReplaceWith { id: ElementId(3,), m: 1 }, + ] + ); +}