Fix diffing Option<String> (#2746)

This commit is contained in:
Evan Almloff 2024-07-30 22:05:56 +02:00 committed by GitHub
parent 115cc0ad42
commit 33f3d40a49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 101 additions and 3 deletions

View file

@ -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>,

View file

@ -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

View file

@ -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 },
]
);
}