chore: dynamic attributes cleanup

This commit is contained in:
Jonathan Kelley 2022-11-27 00:22:39 -05:00
parent bffb2644a3
commit 399169800d
6 changed files with 130 additions and 61 deletions

View file

@ -1,4 +1,4 @@
use crate::{nodes::VNode, virtual_dom::VirtualDom};
use crate::{nodes::VNode, virtual_dom::VirtualDom, Mutations, ScopeId};
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
@ -21,7 +21,7 @@ impl ElementRef {
}
}
impl VirtualDom {
impl<'b> VirtualDom {
pub fn next_element(&mut self, template: &VNode, path: &'static [u8]) -> ElementId {
let entry = self.elements.vacant_entry();
let id = entry.key();
@ -39,11 +39,40 @@ impl VirtualDom {
pub fn cleanup_element(&mut self, id: ElementId) {
self.elements.remove(id.0);
}
pub fn drop_scope(&mut self, id: ScopeId) {
// let scope = self.scopes.get(id.0).unwrap();
// let root = scope.root_node();
// let root = unsafe { std::mem::transmute(root) };
// self.drop_template(root, false);
todo!()
}
pub fn reclaim(&mut self, el: ElementId) {
assert_ne!(el, ElementId(0));
self.elements.remove(el.0);
}
pub fn drop_template(
&mut self,
mutations: &mut Mutations,
template: &'b VNode<'b>,
gen_roots: bool,
) {
// for node in template.dynamic_nodes.iter() {
// match node {
// DynamicNode::Text { id, .. } => {}
// DynamicNode::Component { .. } => {
// todo!()
// }
// DynamicNode::Fragment { inner, nodes } => {}
// DynamicNode::Placeholder(_) => todo!(),
// _ => todo!(),
// }
// }
}
}
/*
now......
an ID is mostly a pointer to a node in the real dom.
We need to
*/

View file

@ -80,7 +80,15 @@ impl<'b: 'static> VirtualDom {
// Else, it's deep in the template and we should create a new id for it
let id = match path.len() {
1 => this_id,
_ => self.next_element(template, template.template.attr_paths[attr_id]),
_ => {
let id = self
.next_element(template, template.template.attr_paths[attr_id]);
self.mutations.push(Mutation::AssignId {
path: &path[1..],
id,
});
id
}
};
loop {

View file

@ -301,14 +301,24 @@ impl<'b: 'static> VirtualDom {
};
}
// we also need to clean up dynamic attribute roots
// let last_node = None;
// for attr in node.dynamic_attrs {
// match last_node {
// Some(node) => todo!(),
// None => todo!(),
// }
// }
// we clean up nodes with dynamic attributes, provided the node is unique and not a root node
let mut id = None;
for (idx, attr) in node.dynamic_attrs.into_iter().enumerate() {
// We'll clean up the root nodes either way, so don't worry
if node.template.attr_paths[idx].len() == 1 {
continue;
}
let next_id = attr.mounted_element.get();
if id == Some(next_id) {
continue;
}
id = Some(next_id);
self.reclaim(next_id);
}
}
fn remove_root_node(&mut self, node: &'b VNode<'b>, idx: usize) {

View file

@ -1,42 +0,0 @@
use crate::{
nodes::VNode, scopes::ScopeId, virtual_dom::VirtualDom, DynamicNode, ElementId, Mutations,
};
impl<'b> VirtualDom {
pub fn drop_scope(&mut self, id: ScopeId) {
// let scope = self.scopes.get(id.0).unwrap();
// let root = scope.root_node();
// let root = unsafe { std::mem::transmute(root) };
// self.drop_template(root, false);
todo!()
}
pub fn reclaim(&mut self, el: ElementId) {
assert_ne!(el, ElementId(0));
println!("reclaiming {}", el.0);
self.elements.remove(el.0);
}
pub fn drop_template(
&mut self,
mutations: &mut Mutations,
template: &'b VNode<'b>,
gen_roots: bool,
) {
// for node in template.dynamic_nodes.iter() {
// match node {
// DynamicNode::Text { id, .. } => {}
// DynamicNode::Component { .. } => {
// todo!()
// }
// DynamicNode::Fragment { inner, nodes } => {}
// DynamicNode::Placeholder(_) => todo!(),
// _ => todo!(),
// }
// }
}
}

View file

@ -8,7 +8,6 @@ mod error_boundary;
mod events;
mod factory;
mod fragment;
mod garbage;
mod lazynodes;
mod mutations;
mod nodes;

View file

@ -0,0 +1,65 @@
//! dynamic attributes in dioxus necessitate an allocated node ID.
//!
//! This tests to ensure we clean it up
use dioxus::core::{ElementId, Mutation::*};
use dioxus::prelude::*;
#[test]
fn attrs_cycle() {
let mut dom = VirtualDom::new(|cx| {
let id = cx.generation();
match cx.generation() % 2 {
0 => cx.render(rsx! {
div {}
}),
1 => cx.render(rsx! {
div {
h1 { class: "{id}", id: "{id}" }
}
}),
_ => unreachable!(),
}
});
assert_eq!(
dom.rebuild().santize().edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
AppendChildren { m: 1 },
]
);
dom.mark_dirty_scope(ScopeId(0));
assert_eq!(
dom.render_immediate().santize().edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
AssignId { path: &[0,], id: ElementId(3,) },
SetAttribute { name: "class", value: "1", id: ElementId(3,), ns: None },
SetAttribute { name: "id", value: "1", id: ElementId(3,), ns: None },
ReplaceWith { id: ElementId(1,), m: 1 },
]
);
dom.mark_dirty_scope(ScopeId(0));
dbg!(
dom.render_immediate().santize(),
[
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
ReplaceWith { id: ElementId(2,), m: 1 },
]
);
dom.mark_dirty_scope(ScopeId(0));
assert_eq!(
dom.render_immediate().santize().edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
AssignId { path: &[0], id: ElementId(1) },
SetAttribute { name: "class", value: "3", id: ElementId(1), ns: None },
SetAttribute { name: "id", value: "3", id: ElementId(1), ns: None },
ReplaceWith { id: ElementId(3), m: 1 }
]
);
}