diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 197550a3b..a108f3577 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -312,19 +312,26 @@ pub struct VComponent<'a> { /// Internally, this is used as a guarantee. Externally, this might be incorrect, so don't count on it. /// /// This flag is assumed by the [`crate::Properties`] trait which is unsafe to implement - pub static_props: bool, + pub(crate) static_props: bool, /// The assigned Scope for this component - pub scope: Cell>, + pub(crate) scope: Cell>, /// The function pointer of the component, known at compile time /// /// It is possible that components get folded at comppile time, so these shouldn't be really used as a key - pub render_fn: *const (), + pub(crate) render_fn: *const (), pub(crate) props: RefCell + 'a>>>, } +impl<'a> VComponent<'a> { + /// Get the scope that this component is mounted to + pub fn mounted_scope(&self) -> Option { + self.scope.get() + } +} + impl<'a> std::fmt::Debug for VComponent<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("VComponent") @@ -342,14 +349,36 @@ pub struct VText<'a> { pub value: &'a str, /// The ID of this node in the real DOM - pub id: Cell>, + pub(crate) id: Cell>, +} + +impl<'a> VText<'a> { + /// Create a new VText + pub fn new(value: &'a str) -> Self { + Self { + value, + id: Default::default(), + } + } + + /// Get the mounted ID of this node + pub fn mounted_element(&self) -> Option { + self.id.get() + } } /// A placeholder node, used by suspense and fragments #[derive(Debug, Default)] pub struct VPlaceholder { /// The ID of this node in the real DOM - pub id: Cell>, + pub(crate) id: Cell>, +} + +impl VPlaceholder { + /// Get the mounted ID of this node + pub fn mounted_element(&self) -> Option { + self.id.get() + } } /// An attribute of the TemplateNode, created at compile time @@ -399,11 +428,34 @@ pub struct Attribute<'a> { /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups. pub namespace: Option<&'static str>, - /// The element in the DOM that this attribute belongs to - pub mounted_element: Cell, - /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated pub volatile: bool, + + /// The element in the DOM that this attribute belongs to + pub(crate) mounted_element: Cell, +} + +impl<'a> Attribute<'a> { + /// Create a new attribute + pub fn new( + name: &'a str, + value: AttributeValue<'a>, + namespace: Option<&'static str>, + volatile: bool, + ) -> Self { + Self { + name, + value, + namespace, + volatile, + mounted_element: Cell::new(ElementId::default()), + } + } + + /// Get the element that this attribute is mounted to + pub fn mounted_element(&self) -> ElementId { + self.mounted_element.get() + } } /// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements diff --git a/packages/core/tests/fuzzing.rs b/packages/core/tests/fuzzing.rs index f193aa2f7..40bb5dcbf 100644 --- a/packages/core/tests/fuzzing.rs +++ b/packages/core/tests/fuzzing.rs @@ -217,23 +217,16 @@ fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute { 5 => AttributeValue::None, 6 => { let value = cx.listener(|e: Event| println!("{:?}", e)); - return Attribute { - name: "ondata", - value, - namespace: None, - mounted_element: Default::default(), - volatile: false, - }; + return Attribute::new("ondata", value, None, false); } _ => unreachable!(), }; - Attribute { - name: Box::leak(format!("attr{}", rand::random::()).into_boxed_str()), + Attribute::new( + Box::leak(format!("attr{}", rand::random::()).into_boxed_str()), value, - namespace: random_ns(), - mounted_element: Default::default(), - volatile: rand::random(), - } + random_ns(), + rand::random(), + ) } static mut TEMPLATE_COUNT: usize = 0; @@ -288,12 +281,9 @@ fn create_random_element(cx: Scope) -> Element { let dynamic_nodes: Vec<_> = dynamic_node_types .iter() .map(|ty| match ty { - DynamicNodeType::Text => DynamicNode::Text(VText { - value: Box::leak( - format!("{}", rand::random::()).into_boxed_str(), - ), - id: Default::default(), - }), + DynamicNodeType::Text => DynamicNode::Text(VText::new(Box::leak( + format!("{}", rand::random::()).into_boxed_str(), + ))), DynamicNodeType::Other => { create_random_dynamic_node(cx, cx.props.depth + 1) } diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 0423e52cf..c59149493 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -10,15 +10,14 @@ macro_rules! impl_event { $( #[$attr] )* #[inline] pub fn $name<'a, E: crate::EventReturn, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute<'a> { - ::dioxus_core::Attribute { - name: stringify!($name), - value: _cx.listener(move |e: ::dioxus_core::Event<$data>| { + ::dioxus_core::Attribute::new( + stringify!($name), + _cx.listener(move |e: ::dioxus_core::Event<$data>| { _f(e).spawn(_cx); }), - namespace: None, - mounted_element: Default::default(), - volatile: false, - } + None, + false, + ) } )* }; diff --git a/packages/native-core/tests/fuzzing.rs b/packages/native-core/tests/fuzzing.rs index 5acdc33c0..1d1bd881c 100644 --- a/packages/native-core/tests/fuzzing.rs +++ b/packages/native-core/tests/fuzzing.rs @@ -217,13 +217,12 @@ fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute { // Listener(RefCell>>), _ => unreachable!(), }; - Attribute { - name: Box::leak(format!("attr{}", rand::random::()).into_boxed_str()), + Attribute::new( + Box::leak(format!("attr{}", rand::random::()).into_boxed_str()), value, - namespace: random_ns(), - mounted_element: Default::default(), - volatile: rand::random(), - } + random_ns(), + rand::random(), + ) } static mut TEMPLATE_COUNT: usize = 0; @@ -263,12 +262,9 @@ fn create_random_element(cx: Scope) -> Element { let dynamic_nodes: Vec<_> = dynamic_node_types .iter() .map(|ty| match ty { - DynamicNodeType::Text => DynamicNode::Text(VText { - value: Box::leak( - format!("{}", rand::random::()).into_boxed_str(), - ), - id: Default::default(), - }), + DynamicNodeType::Text => DynamicNode::Text(VText::new(Box::leak( + format!("{}", rand::random::()).into_boxed_str(), + ))), DynamicNodeType::Other => { create_random_dynamic_node(cx, cx.props.depth + 1) } diff --git a/packages/ssr/src/renderer.rs b/packages/ssr/src/renderer.rs index a85a3ea7e..f23e654fb 100644 --- a/packages/ssr/src/renderer.rs +++ b/packages/ssr/src/renderer.rs @@ -102,7 +102,7 @@ impl Renderer { if self.skip_components { write!(buf, "<{}><{}/>", node.name, node.name)?; } else { - let id = node.scope.get().unwrap(); + let id = node.mounted_scope().unwrap(); let scope = dom.get_scope(id).unwrap(); let node = scope.root_node(); match node { diff --git a/packages/web/src/rehydrate.rs b/packages/web/src/rehydrate.rs index 5cc9ea57e..c7ea6d87d 100644 --- a/packages/web/src/rehydrate.rs +++ b/packages/web/src/rehydrate.rs @@ -1,7 +1,6 @@ use crate::dom::WebsysDom; use dioxus_core::{ - AttributeValue, DynamicNode, ElementId, ScopeState, TemplateNode, VNode, VPlaceholder, VText, - VirtualDom, + AttributeValue, DynamicNode, ElementId, ScopeState, TemplateNode, VNode, VirtualDom, }; use dioxus_html::event_bubbles; use wasm_bindgen::JsCast; @@ -130,7 +129,7 @@ impl WebsysDom { if let dioxus_core::TemplateAttribute::Dynamic { id } = attr { let attribute = &vnode.dynamic_attrs[*id]; let value = &attribute.value; - let id = attribute.mounted_element.get(); + let id = attribute.mounted_element(); mounted_id = Some(id); let name = attribute.name; if let AttributeValue::Listener(_) = value { @@ -211,7 +210,8 @@ impl WebsysDom { } } match dynamic { - dioxus_core::DynamicNode::Text(VText { id, .. }) => { + dioxus_core::DynamicNode::Text(text) => { + let id = text.mounted_element(); // skip comment separator before node if cfg!(debug_assertions) { assert!(current_child @@ -227,7 +227,7 @@ impl WebsysDom { set_node( hydrated, - id.get().ok_or(VNodeNotInitialized)?, + id.ok_or(VNodeNotInitialized)?, current_child.clone()?, ); *current_child = current_child @@ -251,10 +251,10 @@ impl WebsysDom { *last_node_was_static_text = false; } - dioxus_core::DynamicNode::Placeholder(VPlaceholder { id, .. }) => { + dioxus_core::DynamicNode::Placeholder(placeholder) => { set_node( hydrated, - id.get().ok_or(VNodeNotInitialized)?, + placeholder.mounted_element().ok_or(VNodeNotInitialized)?, current_child.clone()?, ); *current_child = current_child @@ -265,7 +265,7 @@ impl WebsysDom { *last_node_was_static_text = false; } dioxus_core::DynamicNode::Component(comp) => { - let scope = comp.scope.get().ok_or(VNodeNotInitialized)?; + let scope = comp.mounted_scope().ok_or(VNodeNotInitialized)?; self.rehydrate_scope( dom.get_scope(scope).unwrap(), current_child,