Prechádzať zdrojové kódy

Fix diffing Option<String> (#2746)

Evan Almloff 11 mesiacov pred
rodič
commit
33f3d40a49

+ 11 - 2
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>,

+ 43 - 1
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

+ 47 - 0
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 },
+        ]
+    );
+}