Browse Source

WIP update tests

Evan Almloff 3 years ago
parent
commit
92010b0bab

+ 65 - 19
packages/native-core-macro/src/lib.rs

@@ -5,7 +5,9 @@ use quote::{quote, ToTokens};
 use syn::{
     self,
     parse::{Parse, ParseStream},
-    Field, Ident, Token, Type,
+    punctuated::Punctuated,
+    token::Paren,
+    Field, Ident, Token, Type, TypeTuple,
 };
 
 #[derive(PartialEq)]
@@ -103,8 +105,9 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
 
     let gen = quote! {
         impl State for #type_name{
-            fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VElement<'a>, ctx: &anymap::AnyMap) -> bool{
-                use dioxus_native_core::real_dom_new_api::NodeDepState;
+            fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: Option<&'a dioxus_core::VElement<'a>>, ctx: &anymap::AnyMap) -> bool{
+                use dioxus_native_core::real_dom_new_api::NodeDepState as _;
+                // println!("called update_node_dep_state with ty: {:?}", ty);
                 if false {
                     unreachable!();
                 }
@@ -114,8 +117,9 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
                 }
             }
 
-            fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VElement<'a>, parent: &Self, ctx: &anymap::AnyMap) -> bool{
-                use dioxus_native_core::real_dom_new_api::ParentDepState;
+            fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: Option<&'a dioxus_core::VElement<'a>>, parent: Option<&Self>, ctx: &anymap::AnyMap) -> bool{
+                use dioxus_native_core::real_dom_new_api::ParentDepState as _;
+                // println!("called update_parent_dep_state with ty: {:?}", ty);
                 if false {
                     unreachable!();
                 }
@@ -125,8 +129,9 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
                 }
             }
 
-            fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VElement<'a>, children: Vec<&Self>, ctx: &anymap::AnyMap) -> bool{
-                use dioxus_native_core::real_dom_new_api::ChildDepState;
+            fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: Option<&'a dioxus_core::VElement<'a>>, children: &[&Self], ctx: &anymap::AnyMap) -> bool{
+                use dioxus_native_core::real_dom_new_api::ChildDepState as _;
+                // println!("called update_child_dep_state with ty: {:?}", ty);
                 if false {
                     unreachable!()
                 }
@@ -200,12 +205,34 @@ struct DepTypes {
 }
 
 impl Parse for DepTypes {
-    fn parse(input: ParseStream) -> Result<DepTypes, syn::Error> {
-        let ctx_ty = input.parse::<Type>().ok();
-        let comma = input.parse::<Token![,]>().ok();
-        let dep_ty = input.parse::<Type>().ok();
-        let dep_ty = comma.and(dep_ty);
-        Ok(DepTypes { ctx_ty, dep_ty })
+    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
+        let dep_ty = input.parse().ok();
+        let comma: Option<Token![,]> = input.parse().ok();
+        let ctx_ty = input.parse().ok();
+        Ok(Self {
+            ctx_ty: comma.and(ctx_ty),
+            dep_ty,
+        })
+    }
+}
+
+struct NodeDepTypes {
+    ctx_ty: Option<Type>,
+}
+
+impl Parse for NodeDepTypes {
+    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
+        let ctx_ty = input.parse().ok();
+        Ok(Self { ctx_ty })
+    }
+}
+
+impl From<NodeDepTypes> for DepTypes {
+    fn from(node_dep_types: NodeDepTypes) -> Self {
+        Self {
+            ctx_ty: node_dep_types.ctx_ty,
+            dep_ty: None,
+        }
     }
 }
 
@@ -243,7 +270,10 @@ impl<'a> StateMember<'a> {
                     _ => None,
                 })
                 .flatten()?;
-            let deps: DepTypes = a.parse_args().ok()?;
+            let deps: DepTypes = match dep_kind {
+                DepKind::NodeDepState => a.parse_args::<NodeDepTypes>().ok()?.into(),
+                _ => a.parse_args().ok()?,
+            };
 
             Some(Self {
                 mem,
@@ -260,8 +290,19 @@ impl<'a> StateMember<'a> {
     fn reduce_self(&self) -> quote::__private::TokenStream {
         let ident = &self.mem.ident;
         let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
-            let msg = ctx_ty.to_token_stream().to_string() + " not found in context";
-            quote! {ctx.get().expect(#msg)}
+            if ctx_ty
+                == &Type::Tuple(TypeTuple {
+                    paren_token: Paren {
+                        span: quote::__private::Span::call_site(),
+                    },
+                    elems: Punctuated::new(),
+                })
+            {
+                quote! {&()}
+            } else {
+                let msg = ctx_ty.to_token_stream().to_string() + " not found in context";
+                quote! {ctx.get().expect(#msg)}
+            }
         } else {
             quote! {&()}
         };
@@ -276,7 +317,7 @@ impl<'a> StateMember<'a> {
                     quote!(self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident).collect(), #get_ctx))
                 }
                 DepKind::ParentDepState => {
-                    quote!(self.#ident.reduce(#node_view, &parent.#dep_ident, #get_ctx))
+                    quote!(self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx))
                 }
             }
         } else {
@@ -288,7 +329,7 @@ impl<'a> StateMember<'a> {
                     quote!(self.#ident.reduce(#node_view, &(), #get_ctx))
                 }
                 DepKind::ParentDepState => {
-                    quote!(self.#ident.reduce(#node_view, &(), #get_ctx))
+                    quote!(self.#ident.reduce(#node_view, Some(&()), #get_ctx))
                 }
             }
         }
@@ -296,6 +337,11 @@ impl<'a> StateMember<'a> {
 
     fn type_id(&self) -> quote::__private::TokenStream {
         let ty = &self.mem.ty;
-        quote!(std::any::TypeId::of::<#ty>())
+        // quote!(std::any::TypeId::of::<#ty>())
+        quote!({
+            let type_id = std::any::TypeId::of::<#ty>();
+            // println!("{:?}", type_id);
+            type_id
+        })
     }
 }

+ 1 - 0
packages/native-core/src/lib.rs

@@ -1,3 +1,4 @@
 pub mod layout_attributes;
 pub mod real_dom;
 pub mod real_dom_new_api;
+pub use dioxus_native_core_macro;

+ 184 - 90
packages/native-core/src/real_dom.rs

@@ -1,13 +1,14 @@
 use anymap::AnyMap;
 use fxhash::{FxHashMap, FxHashSet};
 use std::{
+    any::TypeId,
     collections::VecDeque,
     ops::{Index, IndexMut},
 };
 
 use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
 
-use crate::real_dom_new_api::{State, NodeMask};
+use crate::real_dom_new_api::{AttributeMask, NodeMask, State};
 
 /// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
 /// The render state passes from parent to children and or accumulates state from children to parents.
@@ -47,7 +48,7 @@ impl<S: State> RealDom<S> {
     }
 
     /// Updates the dom, up and down state and return a set of nodes that were updated pass this to update_state.
-    pub fn apply_mutations(&mut self, mutations_vec: Vec<Mutations>) -> Vec<usize> {
+    pub fn apply_mutations(&mut self, mutations_vec: Vec<Mutations>) -> Vec<(usize, NodeMask)> {
         let mut nodes_updated = Vec::new();
         for mutations in mutations_vec {
             for e in mutations.edits {
@@ -69,7 +70,7 @@ impl<S: State> RealDom<S> {
                             .collect();
                         for ns in drained {
                             self.link_child(ns, target).unwrap();
-                            nodes_updated.push(ns);
+                            nodes_updated.push((ns, NodeMask::ALL));
                         }
                     }
                     ReplaceWith { root, m } => {
@@ -77,7 +78,7 @@ impl<S: State> RealDom<S> {
                         let target = root.parent.unwrap().0;
                         let drained: Vec<_> = self.node_stack.drain(0..m as usize).collect();
                         for ns in drained {
-                            nodes_updated.push(ns);
+                            nodes_updated.push((ns, NodeMask::ALL));
                             self.link_child(ns, target).unwrap();
                         }
                     }
@@ -85,7 +86,7 @@ impl<S: State> RealDom<S> {
                         let target = self[root as usize].parent.unwrap().0;
                         let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
                         for ns in drained {
-                            nodes_updated.push(ns);
+                            nodes_updated.push((ns, NodeMask::ALL));
                             self.link_child(ns, target).unwrap();
                         }
                     }
@@ -93,13 +94,13 @@ impl<S: State> RealDom<S> {
                         let target = self[root as usize].parent.unwrap().0;
                         let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
                         for ns in drained {
-                            nodes_updated.push(ns);
+                            nodes_updated.push((ns, NodeMask::ALL));
                             self.link_child(ns, target).unwrap();
                         }
                     }
                     Remove { root } => {
                         if let Some(parent) = self[root as usize].parent {
-                            nodes_updated.push(parent.0);
+                            nodes_updated.push((parent.0, NodeMask::NONE));
                         }
                         self.remove(root as usize).unwrap();
                     }
@@ -165,7 +166,7 @@ impl<S: State> RealDom<S> {
                         text: new_text,
                     } => {
                         let target = &mut self[root as usize];
-                        nodes_updated.push(root as usize);
+                        nodes_updated.push((root as usize, NodeMask::NONE));
                         match &mut target.node_type {
                             NodeType::Text { text } => {
                                 *text = new_text.to_string();
@@ -173,11 +174,19 @@ impl<S: State> RealDom<S> {
                             _ => unreachable!(),
                         }
                     }
-                    SetAttribute { root, .. } => {
-                        nodes_updated.push(root as usize);
+                    SetAttribute { root, field, .. } => {
+                        nodes_updated.push((
+                            root as usize,
+                            NodeMask::new(AttributeMask::single(field), false, false),
+                        ));
                     }
-                    RemoveAttribute { root, .. } => {
-                        nodes_updated.push(root as usize);
+                    RemoveAttribute {
+                        root, name: field, ..
+                    } => {
+                        nodes_updated.push((
+                            root as usize,
+                            NodeMask::new(AttributeMask::single(field), false, false),
+                        ));
                     }
                     PopRoot {} => {
                         self.node_stack.pop();
@@ -193,77 +202,148 @@ impl<S: State> RealDom<S> {
     pub fn update_state(
         &mut self,
         vdom: &VirtualDom,
-        nodes_updated: Vec<usize>,
+        nodes_updated: Vec<(usize, NodeMask)>,
         ctx: AnyMap,
     ) -> Option<FxHashSet<usize>> {
+        #[derive(PartialEq, Clone, Debug)]
+        enum StatesToCheck {
+            All,
+            Some(Vec<TypeId>),
+        }
+
         let mut to_rerender = FxHashSet::default();
-        to_rerender.extend(nodes_updated.iter());
+        to_rerender.extend(nodes_updated.iter().map(|(id, _)| id));
         let mut nodes_updated: Vec<_> = nodes_updated
             .into_iter()
-            .map(|id| (id, self[id].height))
+            .map(|(id, mask)| (id, self[id].height, mask, StatesToCheck::All))
             .collect();
         // Sort nodes first by height, then if the height is the same id.
         nodes_updated.sort_by(|fst, snd| fst.1.cmp(&snd.1).then(fst.0.cmp(&snd.0)));
-        nodes_updated.dedup();
+        {
+            // Combine mutations that affect the same node.
+            let current_key = None;
+            for i in 0..nodes_updated.len() {
+                let current = nodes_updated;
+            }
+        }
+        println!("{:?}", nodes_updated);
+
+        // update the state that only depends on nodes. The order does not matter.
+        for (id, _height, mask, to_check) in &nodes_updated {
+            let mut changed = false;
+            let node = &mut self[*id as usize];
+            let ids = match to_check {
+                StatesToCheck::All => node.state.node_dep_types(&mask),
+                StatesToCheck::Some(_) => unreachable!(),
+            };
+            for ty in ids {
+                let node = &mut self[*id as usize];
+                let el = if let &VNode::Element(e) = node.element(vdom) {
+                    Some(e)
+                } else {
+                    None
+                };
+                changed |= node.state.update_node_dep_state(ty, el, &ctx);
+            }
+            if changed {
+                to_rerender.insert(*id);
+            }
+        }
 
         // bubble up state. To avoid calling reduce more times than nessisary start from the bottom and go up.
         let mut to_bubble: VecDeque<_> = nodes_updated.clone().into();
-        while let Some((id, height)) = to_bubble.pop_back() {
-            let node = &mut self[id as usize];
-            let vnode = node.element(vdom);
-            let node_type = &node.node_type;
-            if let
-                NodeType::Element { children, tag, namespace } =node.node_type{
-
+        while let Some((id, height, mask, to_check)) = to_bubble.pop_back() {
+            let (node, children) = self.get_node_children_mut(id).unwrap();
+            let children_state: Vec<_> = children.iter().map(|c| &c.state).collect();
+            let ids = match to_check {
+                StatesToCheck::All => node.state.child_dep_types(&mask),
+                StatesToCheck::Some(ids) => ids,
+            };
+            let mut changed = Vec::new();
+            for ty in ids {
+                let el = if let &VNode::Element(e) = node.element(vdom) {
+                    Some(e)
+                } else {
+                    None
+                };
+                if node
+                    .state
+                    .update_child_dep_state(ty, el, &children_state, &ctx)
+                {
+                    changed.push(ty);
                 }
-            let mask = NodeMask::new(attritutes, tag, namespace)
-            // todo: reduce cloning state
-            for id in node.state.child_dep_types(mask) {}
-            if new != old {
-                to_rerender.insert(id);
-                if let Some(p) = parent {
-                    let i = to_bubble.partition_point(|(other_id, h)| {
-                        *h < height - 1 || (*h == height - 1 && *other_id < p.0)
+            }
+            if let Some(parent_id) = node.parent {
+                if !changed.is_empty() {
+                    to_rerender.insert(id);
+                    let i = to_bubble.partition_point(|(other_id, h, ..)| {
+                        *h < height - 1 || (*h == height - 1 && *other_id < parent_id.0)
                     });
                     // make sure the parent is not already queued
-                    if i >= to_bubble.len() || to_bubble[i].0 != p.0 {
-                        to_bubble.insert(i, (p.0, height - 1));
+                    if i >= to_bubble.len() || to_bubble[i].0 != parent_id.0 {
+                        to_bubble.insert(
+                            i,
+                            (
+                                parent_id.0,
+                                height - 1,
+                                NodeMask::NONE,
+                                StatesToCheck::Some(changed),
+                            ),
+                        );
                     }
                 }
-                let node = &mut self[id as usize];
-                node.up_state = new;
             }
         }
 
         // push down state. To avoid calling reduce more times than nessisary start from the top and go down.
         let mut to_push: VecDeque<_> = nodes_updated.clone().into();
-        while let Some((id, height)) = to_push.pop_front() {
-            let node = &self[id as usize];
-            // todo: reduce cloning state
-            let old = node.down_state.clone();
-            let mut new = node.down_state.clone();
-            let vnode = node.element(vdom);
-            new.reduce(
-                node.parent
-                    .filter(|e| e.0 != 0)
-                    .map(|e| &self[e].down_state),
-                vnode,
-                ds_ctx,
-            );
-            if new != old {
-                to_rerender.insert(id);
-                let node = &mut self[id as usize];
+        while let Some((id, height, mask, to_check)) = to_push.pop_front() {
+            let node = &self[id];
+            let ids = match to_check {
+                StatesToCheck::All => node.state.parent_dep_types(&mask),
+                StatesToCheck::Some(ids) => ids,
+            };
+            let mut changed = Vec::new();
+            let (node, parent) = self.get_node_parent_mut(id).unwrap();
+            for ty in ids {
+                let el = if let &VNode::Element(e) = node.element(vdom) {
+                    Some(e)
+                } else {
+                    None
+                };
+                let parent = parent.as_deref();
+                let state = &mut node.state;
+                if state.update_parent_dep_state(
+                    ty,
+                    el,
+                    parent.filter(|n| n.id.0 != 0).map(|n| &n.state),
+                    &ctx,
+                ) {
+                    changed.push(ty);
+                }
+            }
+
+            to_rerender.insert(id);
+            if !changed.is_empty() {
+                let node = &self[id];
                 if let NodeType::Element { children, .. } = &node.node_type {
                     for c in children {
-                        let i = to_push.partition_point(|(other_id, h)| {
+                        let i = to_push.partition_point(|(other_id, h, ..)| {
                             *h < height + 1 || (*h == height + 1 && *other_id < c.0)
                         });
                         if i >= to_push.len() || to_push[i].0 != c.0 {
-                            to_push.insert(i, (c.0, height + 1));
+                            to_push.insert(
+                                i,
+                                (
+                                    c.0,
+                                    height + 1,
+                                    NodeMask::NONE,
+                                    StatesToCheck::Some(changed.clone()),
+                                ),
+                            );
                         }
                     }
                 }
-                node.down_state = new;
             }
         }
 
@@ -333,6 +413,56 @@ impl<S: State> RealDom<S> {
         self.nodes.get_mut(id)?.as_mut()
     }
 
+    // this is safe because no node will have itself as a child
+    pub fn get_node_children_mut<'a>(
+        &'a mut self,
+        id: usize,
+    ) -> Option<(&'a mut Node<S>, Vec<&'a mut Node<S>>)> {
+        let ptr = self.nodes.as_mut_ptr();
+        unsafe {
+            if id >= self.nodes.len() {
+                None
+            } else {
+                let node = &mut *ptr.add(id);
+                if let Some(node) = node.as_mut() {
+                    let children = match &node.node_type {
+                        NodeType::Element { children, .. } => children
+                            .iter()
+                            .map(|id| (&mut *ptr.add(id.0)).as_mut().unwrap())
+                            .collect(),
+                        _ => Vec::new(),
+                    };
+                    return Some((node, children));
+                } else {
+                    None
+                }
+            }
+        }
+    }
+
+    // this is safe because no node will have itself as a parent
+    pub fn get_node_parent_mut<'a>(
+        &'a mut self,
+        id: usize,
+    ) -> Option<(&'a mut Node<S>, Option<&'a mut Node<S>>)> {
+        let ptr = self.nodes.as_mut_ptr();
+        unsafe {
+            let node = &mut *ptr.add(id);
+            if id >= self.nodes.len() {
+                None
+            } else {
+                if let Some(node) = node.as_mut() {
+                    let parent = node
+                        .parent
+                        .map(|id| (&mut *ptr.add(id.0)).as_mut().unwrap());
+                    return Some((node, parent));
+                } else {
+                    None
+                }
+            }
+        }
+    }
+
     pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S>> {
         if let Some(nodes) = self.nodes_listening.get(event) {
             let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
@@ -528,39 +658,3 @@ impl<S: State> Node<S> {
         self.parent = Some(parent);
     }
 }
-
-/// This state that is passed down to children. For example text properties (`<b>` `<i>` `<u>`) would be passed to children.
-/// Called when the current node's node properties are modified or a parrent's [PushedDownState] is modified.
-/// Called at most once per update.
-pub trait PushedDownState: Default + PartialEq + Clone {
-    /// The context is passed to the [PushedDownState::reduce] when it is pushed down.
-    /// This is sometimes nessisary for lifetime purposes.
-    type Ctx;
-    fn reduce(&mut self, parent: Option<&Self>, vnode: &VNode, ctx: &mut Self::Ctx);
-}
-impl PushedDownState for () {
-    type Ctx = ();
-    fn reduce(&mut self, _parent: Option<&Self>, _vnode: &VNode, _ctx: &mut Self::Ctx) {}
-}
-
-/// This state is derived from children. For example a node's size could be derived from the size of children.
-/// Called when the current node's node properties are modified, a child's [BubbledUpState] is modified or a child is removed.
-/// Called at most once per update.
-pub trait BubbledUpState: Default + PartialEq + Clone {
-    /// The context is passed to the [BubbledUpState::reduce] when it is bubbled up.
-    /// This is sometimes nessisary for lifetime purposes.
-    type Ctx;
-    fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, ctx: &mut Self::Ctx)
-    where
-        I: Iterator<Item = &'a Self>,
-        Self: 'a;
-}
-impl BubbledUpState for () {
-    type Ctx = ();
-    fn reduce<'a, I>(&mut self, _children: I, _vnode: &VNode, _ctx: &mut Self::Ctx)
-    where
-        I: Iterator<Item = &'a Self>,
-        Self: 'a,
-    {
-    }
-}

+ 170 - 43
packages/native-core/src/real_dom_new_api.rs

@@ -3,12 +3,13 @@ use std::any::TypeId;
 use anymap::AnyMap;
 use dioxus_core::{Attribute, VElement};
 
+#[derive(Debug)]
 pub struct NodeView<'a> {
-    inner: &'a VElement<'a>,
+    inner: Option<&'a VElement<'a>>,
     view: NodeMask,
 }
 impl<'a> NodeView<'a> {
-    pub fn new(velement: &'a VElement<'a>, view: NodeMask) -> Self {
+    pub fn new(velement: Option<&'a VElement<'a>>, view: NodeMask) -> Self {
         Self {
             inner: velement,
             view: view,
@@ -17,7 +18,7 @@ impl<'a> NodeView<'a> {
 
     pub fn tag(&self) -> Option<&'a str> {
         if self.view.tag {
-            Some(self.inner.tag)
+            self.inner.map(|el| el.tag)
         } else {
             None
         }
@@ -25,7 +26,7 @@ impl<'a> NodeView<'a> {
 
     pub fn namespace(&self) -> Option<&'a str> {
         if self.view.namespace {
-            self.inner.namespace
+            self.inner.map(|el| el.namespace).flatten()
         } else {
             None
         }
@@ -33,23 +34,159 @@ impl<'a> NodeView<'a> {
 
     pub fn attributes(&self) -> impl Iterator<Item = &Attribute<'a>> {
         self.inner
-            .attributes
+            .map(|el| el.attributes)
+            .unwrap_or_default()
             .iter()
-            .filter(|a| self.view.attritutes.contains(&a.name))
+            .filter(|a| self.view.attritutes.contains_attribute(&a.name))
     }
 }
 
-#[derive(Default)]
+#[derive(PartialEq, Clone, Debug)]
+pub enum AttributeMask {
+    All,
+    Dynamic(Vec<&'static str>),
+    Static(&'static [&'static str]),
+}
+
+impl AttributeMask {
+    pub const NONE: Self = Self::Static(&[]);
+
+    fn contains_attribute(&self, attr: &'static str) -> bool {
+        match self {
+            AttributeMask::All => true,
+            AttributeMask::Dynamic(l) => l.contains(&attr),
+            AttributeMask::Static(l) => l.contains(&attr),
+        }
+    }
+
+    pub fn single(new: &'static str) -> Self {
+        Self::Dynamic(vec![new])
+    }
+
+    pub fn verify(&self) {
+        match &self {
+            AttributeMask::Static(attrs) => debug_assert!(
+                attrs.windows(2).all(|w| w[0] < w[1]),
+                "attritutes must be increasing"
+            ),
+            AttributeMask::Dynamic(attrs) => debug_assert!(
+                attrs.windows(2).all(|w| w[0] < w[1]),
+                "attritutes must be increasing"
+            ),
+            _ => (),
+        }
+    }
+
+    pub fn union(&self, other: &Self) -> Self {
+        pub fn union_iter(
+            s_iter: impl Iterator<Item = &'static str>,
+            o_iter: impl Iterator<Item = &'static str>,
+        ) -> Vec<&'static str> {
+            let mut s_peekable = s_iter.peekable();
+            let mut o_peekable = o_iter.peekable();
+            let mut v = Vec::new();
+            while let Some(s_i) = s_peekable.peek() {
+                loop {
+                    if let Some(o_i) = o_peekable.peek() {
+                        if o_i > s_i {
+                            break;
+                        } else {
+                            v.push(o_peekable.next().unwrap());
+                        }
+                    } else {
+                        break;
+                    }
+                }
+                v.push(s_peekable.next().unwrap());
+            }
+            while let Some(o_i) = o_peekable.next() {
+                v.push(o_i);
+            }
+            v
+        }
+
+        let new = match (self, other) {
+            (AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => {
+                AttributeMask::Dynamic(union_iter(s.iter().copied(), o.iter().copied()))
+            }
+            (AttributeMask::Static(s), AttributeMask::Dynamic(o)) => {
+                AttributeMask::Dynamic(union_iter(s.iter().copied(), o.iter().copied()))
+            }
+            (AttributeMask::Dynamic(s), AttributeMask::Static(o)) => {
+                AttributeMask::Dynamic(union_iter(s.iter().copied(), o.iter().copied()))
+            }
+            (AttributeMask::Static(s), AttributeMask::Static(o)) => {
+                AttributeMask::Dynamic(union_iter(s.iter().copied(), o.iter().copied()))
+            }
+            _ => AttributeMask::All,
+        };
+        new.verify();
+        new
+    }
+
+    fn overlaps(&self, other: &Self) -> bool {
+        fn overlaps_iter(
+            mut self_iter: impl Iterator<Item = &'static str>,
+            mut other_iter: impl Iterator<Item = &'static str>,
+        ) -> bool {
+            if let Some(mut other_attr) = other_iter.next() {
+                while let Some(self_attr) = self_iter.next() {
+                    while other_attr < self_attr {
+                        if let Some(attr) = other_iter.next() {
+                            other_attr = attr;
+                        } else {
+                            return false;
+                        }
+                    }
+                    if other_attr == self_attr {
+                        return true;
+                    }
+                }
+            }
+            false
+        }
+        match (self, other) {
+            (AttributeMask::All, AttributeMask::All) => true,
+            (AttributeMask::All, AttributeMask::Dynamic(v)) => !v.is_empty(),
+            (AttributeMask::All, AttributeMask::Static(s)) => !s.is_empty(),
+            (AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
+            (AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
+            (AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
+                overlaps_iter(v1.iter().copied(), v2.iter().copied())
+            }
+            (AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
+                overlaps_iter(v.iter().copied(), s.iter().copied())
+            }
+            (AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
+                overlaps_iter(v.iter().copied(), s.iter().copied())
+            }
+            (AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
+                overlaps_iter(s1.iter().copied(), s2.iter().copied())
+            }
+        }
+    }
+}
+
+impl Default for AttributeMask {
+    fn default() -> Self {
+        AttributeMask::Static(&[])
+    }
+}
+
+#[derive(Default, PartialEq, Clone, Debug)]
 pub struct NodeMask {
     // must be sorted
-    attritutes: &'static [&'static str],
+    attritutes: AttributeMask,
     tag: bool,
     namespace: bool,
 }
 
 impl NodeMask {
+    pub const NONE: Self = Self::new(AttributeMask::Static(&[]), false, false);
+    pub const ALL: Self = Self::new(AttributeMask::All, true, true);
+
     /// attritutes must be sorted!
-    pub const fn new(attritutes: &'static [&'static str], tag: bool, namespace: bool) -> Self {
+    pub const fn new(attritutes: AttributeMask, tag: bool, namespace: bool) -> Self {
         Self {
             attritutes,
             tag,
@@ -57,13 +194,6 @@ impl NodeMask {
         }
     }
 
-    pub fn verify(&self) {
-        debug_assert!(
-            self.attritutes.windows(2).all(|w| w[0] < w[1]),
-            "attritutes must be increasing"
-        );
-    }
-
     pub fn overlaps(&self, other: &Self) -> bool {
         (self.tag && other.tag)
             || (self.namespace && other.namespace)
@@ -71,51 +201,48 @@ impl NodeMask {
     }
 
     fn attritutes_overlap(&self, other: &Self) -> bool {
-        let mut self_attrs = self.attritutes.iter();
-        let mut other_attrs = other.attritutes.iter();
-        if let Some(mut other_attr) = other_attrs.next() {
-            while let Some(self_attr) = self_attrs.next() {
-                while other_attr < self_attr {
-                    if let Some(attr) = other_attrs.next() {
-                        other_attr = attr;
-                    } else {
-                        return false;
-                    }
-                }
-                if other_attr == self_attr {
-                    return true;
-                }
-            }
-        }
-        false
+        self.attritutes.overlaps(&other.attritutes)
     }
 }
 
+/// This state is derived from children. For example a node's size could be derived from the size of children.
+/// Called when the current node's node properties are modified, a child's [BubbledUpState] is modified or a child is removed.
+/// Called at most once per update.
 pub trait ChildDepState {
+    /// The context is passed to the [PushedDownState::reduce] when it is pushed down.
+    /// This is sometimes nessisary for lifetime purposes.
     type Ctx;
     type DepState: ChildDepState;
-    const NODE_MASK: NodeMask = NodeMask::new(&[], false, false);
+    const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, false, false);
     fn reduce(&mut self, node: NodeView, children: Vec<&Self::DepState>, ctx: &Self::Ctx) -> bool;
 }
 
+/// This state that is passed down to children. For example text properties (`<b>` `<i>` `<u>`) would be passed to children.
+/// Called when the current node's node properties are modified or a parrent's [PushedDownState] is modified.
+/// Called at most once per update.
 pub trait ParentDepState {
+    /// The context is passed to the [PushedDownState::reduce] when it is pushed down.
+    /// This is sometimes nessisary for lifetime purposes.
     type Ctx;
     type DepState: ParentDepState;
-    const NODE_MASK: NodeMask = NodeMask::new(&[], false, false);
-    fn reduce(&mut self, node: NodeView, parent: &Self::DepState, ctx: &Self::Ctx) -> bool;
+    const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, false, false);
+    fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool;
 }
 
+/// This state that is upadated lazily. For example any propertys that do not effect other parts of the dom like bg-color.
+/// Called when the current node's node properties are modified or a parrent's [PushedDownState] is modified.
+/// Called at most once per update.
 pub trait NodeDepState {
     type Ctx;
-    const NODE_MASK: NodeMask = NodeMask::new(&[], false, false);
+    const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, false, false);
     fn reduce(&mut self, node: NodeView, ctx: &Self::Ctx) -> bool;
 }
 
-pub trait State: Default {
+pub trait State: Default + Clone {
     fn update_node_dep_state<'a>(
         &'a mut self,
         ty: TypeId,
-        node: &'a VElement<'a>,
+        node: Option<&'a VElement<'a>>,
         ctx: &AnyMap,
     ) -> bool;
     /// This must be a valid resolution order. (no nodes updated before a state they rely on)
@@ -124,8 +251,8 @@ pub trait State: Default {
     fn update_parent_dep_state<'a>(
         &'a mut self,
         ty: TypeId,
-        node: &'a VElement<'a>,
-        parent: &Self,
+        node: Option<&'a VElement<'a>>,
+        parent: Option<&Self>,
         ctx: &AnyMap,
     ) -> bool;
     /// This must be a valid resolution order. (no nodes updated before a state they rely on)
@@ -134,8 +261,8 @@ pub trait State: Default {
     fn update_child_dep_state<'a>(
         &'a mut self,
         ty: TypeId,
-        node: &'a VElement<'a>,
-        children: Vec<&Self>,
+        node: Option<&'a VElement<'a>>,
+        children: &[&Self],
         ctx: &AnyMap,
     ) -> bool;
     /// This must be a valid resolution order. (no nodes updated before a state they rely on)

+ 7 - 2
packages/native-core/tests/change_nodes.rs

@@ -3,8 +3,13 @@ use dioxus_core::*;
 use dioxus_core_macro::*;
 use dioxus_html as dioxus_elements;
 use dioxus_native_core::real_dom::RealDom;
+use dioxus_native_core::real_dom_new_api::State;
+use dioxus_native_core_macro::State;
 use std::cell::Cell;
 
+#[derive(State, Default, Clone)]
+struct Empty {}
+
 #[test]
 fn remove_node() {
     #[allow(non_snake_case)]
@@ -20,7 +25,7 @@ fn remove_node() {
         }
     });
 
-    let mut dom: RealDom<(), ()> = RealDom::new();
+    let mut dom: RealDom<Empty> = RealDom::new();
 
     let _to_update = dom.apply_mutations(vec![mutations]);
     let child_div = VElement {
@@ -92,7 +97,7 @@ fn add_node() {
         div{}
     });
 
-    let mut dom: RealDom<(), ()> = RealDom::new();
+    let mut dom: RealDom<Empty> = RealDom::new();
 
     let _to_update = dom.apply_mutations(vec![mutations]);
 

+ 7 - 2
packages/native-core/tests/initial_build.rs

@@ -5,6 +5,11 @@ use dioxus_core::*;
 use dioxus_core_macro::*;
 use dioxus_html as dioxus_elements;
 use dioxus_native_core::real_dom::RealDom;
+use dioxus_native_core::real_dom_new_api::State;
+use dioxus_native_core_macro::State;
+
+#[derive(State, Default, Clone)]
+struct Empty {}
 
 #[test]
 fn initial_build_simple() {
@@ -21,7 +26,7 @@ fn initial_build_simple() {
         div{}
     });
 
-    let mut dom: RealDom<(), ()> = RealDom::new();
+    let mut dom: RealDom<Empty> = RealDom::new();
 
     let _to_update = dom.apply_mutations(vec![mutations]);
     let root_div = VElement {
@@ -60,7 +65,7 @@ fn initial_build_with_children() {
         }
     });
 
-    let mut dom: RealDom<(), ()> = RealDom::new();
+    let mut dom: RealDom<Empty> = RealDom::new();
 
     let _to_update = dom.apply_mutations(vec![mutations]);
     let first_text = VText {

+ 7 - 7
packages/native-core/tests/parse.rs

@@ -1,16 +1,16 @@
 use dioxus_native_core::real_dom_new_api::*;
 use dioxus_native_core_macro::*;
 
-#[derive(State)]
+#[derive(State, Default, Clone)]
 struct Z {
     // depends on just attributes and no context
     #[node_dep_state()]
     x: A,
     // depends on attributes, the B component of children and i32 context
-    #[child_dep_state(i32, B)]
+    #[child_dep_state(B, i32)]
     y: B,
     // depends on attributes, the C component of it's parent and a u8 context
-    #[parent_dep_state(u8, C)]
+    #[parent_dep_state(C, u8)]
     z: C,
 }
 
@@ -22,7 +22,7 @@ struct Z {
 
 use dioxus_native_core::real_dom_new_api::NodeDepState;
 
-#[derive(PartialEq)]
+#[derive(Default, Clone)]
 struct A;
 impl NodeDepState for A {
     type Ctx = ();
@@ -31,7 +31,7 @@ impl NodeDepState for A {
     }
 }
 
-#[derive(PartialEq)]
+#[derive(Default, Clone)]
 struct B;
 impl ChildDepState for B {
     type Ctx = i32;
@@ -46,7 +46,7 @@ impl ChildDepState for B {
     }
 }
 
-#[derive(PartialEq)]
+#[derive(Default, Clone)]
 struct C;
 impl ParentDepState for C {
     type Ctx = u8;
@@ -54,7 +54,7 @@ impl ParentDepState for C {
     fn reduce(
         &mut self,
         _: dioxus_native_core::real_dom_new_api::NodeView,
-        _: &Self::DepState,
+        _: Option<&Self::DepState>,
         _: &u8,
     ) -> bool {
         todo!()

+ 148 - 79
packages/native-core/tests/state.rs

@@ -1,60 +1,125 @@
+use anymap::AnyMap;
 use dioxus_core::VNode;
 use dioxus_core::*;
 use dioxus_core_macro::*;
 use dioxus_html as dioxus_elements;
 use dioxus_native_core::real_dom::*;
+use dioxus_native_core::real_dom_new_api::{
+    AttributeMask, ChildDepState, NodeDepState, NodeMask, NodeView, ParentDepState, State,
+};
+use dioxus_native_core_macro::State;
+
+#[derive(Debug, Clone, Default, State)]
+struct CallCounterState {
+    #[child_dep_state(ChildDepCallCounter)]
+    child_counter: ChildDepCallCounter,
+    #[parent_dep_state(ParentDepCallCounter)]
+    parent_counter: ParentDepCallCounter,
+    #[node_dep_state()]
+    node_counter: NodeDepCallCounter,
+}
 
-#[derive(Debug, Clone, PartialEq, Default)]
-struct CallCounter(u32);
-impl BubbledUpState for CallCounter {
+#[derive(Debug, Clone, Default)]
+struct ChildDepCallCounter(u32);
+impl ChildDepState for ChildDepCallCounter {
     type Ctx = ();
-
-    fn reduce<'a, I>(&mut self, _children: I, _vnode: &VNode, _ctx: &mut Self::Ctx)
-    where
-        I: Iterator<Item = &'a Self>,
-        Self: 'a,
-    {
+    type DepState = Self;
+    const NODE_MASK: NodeMask = NodeMask::ALL;
+    fn reduce(
+        &mut self,
+        _node: NodeView,
+        _children: Vec<&Self::DepState>,
+        _ctx: &Self::Ctx,
+    ) -> bool {
         self.0 += 1;
+        true
     }
 }
 
-impl PushedDownState for CallCounter {
+#[derive(Debug, Clone, Default)]
+struct ParentDepCallCounter(u32);
+impl ParentDepState for ParentDepCallCounter {
     type Ctx = ();
+    type DepState = Self;
+    const NODE_MASK: NodeMask = NodeMask::ALL;
+    fn reduce(
+        &mut self,
+        _node: NodeView,
+        _parent: Option<&Self::DepState>,
+        _ctx: &Self::Ctx,
+    ) -> bool {
+        self.0 += 1;
+        true
+    }
+}
 
-    fn reduce(&mut self, _parent: Option<&Self>, _vnode: &VNode, _ctx: &mut Self::Ctx) {
+#[derive(Debug, Clone, Default)]
+struct NodeDepCallCounter(u32);
+impl NodeDepState for NodeDepCallCounter {
+    type Ctx = ();
+    const NODE_MASK: NodeMask = NodeMask::ALL;
+    fn reduce(&mut self, _node: NodeView, _ctx: &Self::Ctx) -> bool {
         self.0 += 1;
+        true
     }
 }
 
 #[derive(Debug, Clone, PartialEq, Default)]
-struct BubbledUpStateTester(String, Vec<Box<BubbledUpStateTester>>);
-impl BubbledUpState for BubbledUpStateTester {
+struct BubbledUpStateTester(Option<String>, Vec<Box<BubbledUpStateTester>>);
+impl ChildDepState for BubbledUpStateTester {
     type Ctx = u32;
-
-    fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, ctx: &mut Self::Ctx)
-    where
-        I: Iterator<Item = &'a Self>,
-        Self: 'a,
-    {
+    type DepState = Self;
+    const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, true, false);
+    fn reduce(&mut self, node: NodeView, children: Vec<&Self::DepState>, ctx: &Self::Ctx) -> bool {
         assert_eq!(*ctx, 42);
         *self = BubbledUpStateTester(
-            vnode.mounted_id().to_string(),
-            children.map(|c| Box::new(c.clone())).collect(),
+            node.tag().map(|s| s.to_string()),
+            children.into_iter().map(|c| Box::new(c.clone())).collect(),
         );
+        true
     }
 }
 
 #[derive(Debug, Clone, PartialEq, Default)]
-struct PushedDownStateTester(String, Option<Box<PushedDownStateTester>>);
-impl PushedDownState for PushedDownStateTester {
+struct PushedDownStateTester(Option<String>, Option<Box<PushedDownStateTester>>);
+impl ParentDepState for PushedDownStateTester {
     type Ctx = u32;
-
-    fn reduce(&mut self, parent: Option<&Self>, vnode: &VNode, ctx: &mut Self::Ctx) {
+    type DepState = Self;
+    const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, true, false);
+    fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool {
         assert_eq!(*ctx, 42);
         *self = PushedDownStateTester(
-            vnode.mounted_id().to_string(),
+            node.tag().map(|s| s.to_string()),
             parent.map(|c| Box::new(c.clone())),
         );
+        true
+    }
+}
+
+#[derive(State, Clone, Default, Debug)]
+struct StateTester {
+    #[child_dep_state(BubbledUpStateTester, u32)]
+    bubbled: BubbledUpStateTester,
+    #[parent_dep_state(PushedDownStateTester, u32)]
+    pushed: PushedDownStateTester,
+    #[node_dep_state(u32)]
+    node: NodeStateTester,
+}
+
+#[derive(Debug, Clone, PartialEq, Default)]
+struct NodeStateTester(Option<String>, Vec<(String, String)>);
+impl NodeDepState for NodeStateTester {
+    type Ctx = u32;
+    const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, false);
+    fn reduce(&mut self, node: NodeView, ctx: &Self::Ctx) -> bool {
+        assert_eq!(*ctx, 42);
+        *self = NodeStateTester(
+            node.tag().map(|s| s.to_string()),
+            node.attributes()
+                .map(|a| (a.name.to_string(), a.value.to_string()))
+                .collect(),
+        );
+        true
     }
 }
 
@@ -72,71 +137,68 @@ fn state_initial() {
 
     let mutations = vdom.create_vnodes(rsx! {
         div {
-            p{}
+            p{
+                color: "red"
+            }
             h1{}
         }
     });
 
-    let mut dom: RealDom<BubbledUpStateTester, PushedDownStateTester> = RealDom::new();
+    let mut dom: RealDom<StateTester> = RealDom::new();
 
     let nodes_updated = dom.apply_mutations(vec![mutations]);
-    let _to_rerender = dom.update_state(&vdom, nodes_updated, &mut 42, &mut 42);
+    let mut ctx = AnyMap::new();
+    ctx.insert(42u32);
+    let _to_rerender = dom.update_state(&vdom, nodes_updated, ctx);
 
     let root_div = &dom[1];
-    assert_eq!(root_div.up_state.0, "1");
+    assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
     assert_eq!(
-        root_div.up_state.1,
+        root_div.state.bubbled.1,
         vec![
-            Box::new(BubbledUpStateTester("2".to_string(), Vec::new())),
-            Box::new(BubbledUpStateTester("3".to_string(), Vec::new()))
+            Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())),
+            Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new()))
         ]
     );
-    assert_eq!(root_div.down_state.0, "1");
-    assert_eq!(root_div.down_state.1, None);
+    assert_eq!(root_div.state.pushed.0, Some("div".to_string()));
+    assert_eq!(root_div.state.pushed.1, None);
+    assert_eq!(root_div.state.node.0, Some("div".to_string()));
+    assert_eq!(root_div.state.node.1, vec![]);
 
     let child_p = &dom[2];
-    assert_eq!(child_p.up_state.0, "2");
-    assert_eq!(child_p.up_state.1, Vec::new());
-    assert_eq!(child_p.down_state.0, "2");
+    assert_eq!(child_p.state.bubbled.0, Some("p".to_string()));
+    assert_eq!(child_p.state.bubbled.1, Vec::new());
+    assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
     assert_eq!(
-        child_p.down_state.1,
-        Some(Box::new(PushedDownStateTester("1".to_string(), None)))
+        child_p.state.pushed.1,
+        Some(Box::new(PushedDownStateTester(
+            Some("div".to_string()),
+            None
+        )))
+    );
+    assert_eq!(child_p.state.node.0, Some("p".to_string()));
+    assert_eq!(
+        child_p.state.node.1,
+        vec![("color".to_string(), "red".to_string())]
     );
 
     let child_h1 = &dom[3];
-    assert_eq!(child_h1.up_state.0, "3");
-    assert_eq!(child_h1.up_state.1, Vec::new());
-    assert_eq!(child_h1.down_state.0, "3");
+    assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string()));
+    assert_eq!(child_h1.state.bubbled.1, Vec::new());
+    assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
     assert_eq!(
-        child_h1.down_state.1,
-        Some(Box::new(PushedDownStateTester("1".to_string(), None)))
+        child_h1.state.pushed.1,
+        Some(Box::new(PushedDownStateTester(
+            Some("div".to_string()),
+            None
+        )))
     );
+    assert_eq!(child_h1.state.node.0, Some("h1".to_string()));
+    assert_eq!(child_h1.state.node.1, vec![]);
 }
 
 #[test]
 fn state_reduce_initally_called_minimally() {
-    #[derive(Debug, Clone, PartialEq, Default)]
-    struct CallCounter(u32);
-    impl BubbledUpState for CallCounter {
-        type Ctx = ();
-
-        fn reduce<'a, I>(&mut self, _children: I, _vnode: &VNode, _ctx: &mut Self::Ctx)
-        where
-            I: Iterator<Item = &'a Self>,
-            Self: 'a,
-        {
-            self.0 += 1;
-        }
-    }
-
-    impl PushedDownState for CallCounter {
-        type Ctx = ();
-
-        fn reduce(&mut self, _parent: Option<&Self>, _vnode: &VNode, _ctx: &mut Self::Ctx) {
-            self.0 += 1;
-        }
-    }
-
     #[allow(non_snake_case)]
     fn Base(cx: Scope) -> Element {
         rsx!(cx, div {
@@ -178,14 +240,15 @@ fn state_reduce_initally_called_minimally() {
         }
     });
 
-    let mut dom: RealDom<CallCounter, CallCounter> = RealDom::new();
+    let mut dom: RealDom<CallCounterState> = RealDom::new();
 
     let nodes_updated = dom.apply_mutations(vec![mutations]);
-    let _to_rerender = dom.update_state(&vdom, nodes_updated, &mut (), &mut ());
+    let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
 
     dom.traverse_depth_first(|n| {
-        assert_eq!(n.up_state.0, 1);
-        assert_eq!(n.down_state.0, 1);
+        assert_eq!(n.state.child_counter.0, 1);
+        assert_eq!(n.state.parent_counter.0, 1);
+        assert_eq!(n.state.node_counter.0, 1);
     });
 }
 
@@ -194,6 +257,7 @@ fn state_reduce_down_called_minimally_on_update() {
     #[allow(non_snake_case)]
     fn Base(cx: Scope) -> Element {
         rsx!(cx, div {
+            width: "100%",
             div{
                 div{
                     p{}
@@ -233,10 +297,10 @@ fn state_reduce_down_called_minimally_on_update() {
         }
     });
 
-    let mut dom: RealDom<CallCounter, CallCounter> = RealDom::new();
+    let mut dom: RealDom<CallCounterState> = RealDom::new();
 
     let nodes_updated = dom.apply_mutations(vec![mutations]);
-    let _to_rerender = dom.update_state(&vdom, nodes_updated, &mut (), &mut ());
+    let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
     let nodes_updated = dom.apply_mutations(vec![Mutations {
         edits: vec![DomEdit::SetAttribute {
             root: 1,
@@ -247,11 +311,13 @@ fn state_reduce_down_called_minimally_on_update() {
         dirty_scopes: fxhash::FxHashSet::default(),
         refs: Vec::new(),
     }]);
-    let _to_rerender = dom.update_state(&vdom, nodes_updated, &mut (), &mut ());
+    let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
 
     dom.traverse_depth_first(|n| {
-        assert_eq!(n.down_state.0, 2);
+        println!("{:?}", n.state);
+        // assert_eq!(n.state.parent_counter.0, 2);
     });
+    panic!()
 }
 
 #[test]
@@ -259,6 +325,7 @@ fn state_reduce_up_called_minimally_on_update() {
     #[allow(non_snake_case)]
     fn Base(cx: Scope) -> Element {
         rsx!(cx, div {
+            width: "100%",
             div{
                 div{
                     p{}
@@ -298,10 +365,10 @@ fn state_reduce_up_called_minimally_on_update() {
         }
     });
 
-    let mut dom: RealDom<CallCounter, CallCounter> = RealDom::new();
+    let mut dom: RealDom<CallCounterState> = RealDom::new();
 
     let nodes_updated = dom.apply_mutations(vec![mutations]);
-    let _to_rerender = dom.update_state(&vdom, nodes_updated, &mut (), &mut ());
+    let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
     let nodes_updated = dom.apply_mutations(vec![Mutations {
         edits: vec![DomEdit::SetAttribute {
             root: 4,
@@ -312,9 +379,11 @@ fn state_reduce_up_called_minimally_on_update() {
         dirty_scopes: fxhash::FxHashSet::default(),
         refs: Vec::new(),
     }]);
-    let _to_rerender = dom.update_state(&vdom, nodes_updated, &mut (), &mut ());
+    let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
 
     dom.traverse_depth_first(|n| {
-        assert_eq!(n.up_state.0, if n.id.0 > 4 { 1 } else { 2 });
+        println!("{:?}", n.state);
+        // assert_eq!(n.state.child_counter.0, if n.id.0 > 4 { 1 } else { 2 });
     });
+    panic!()
 }

+ 1 - 0
packages/tui/Cargo.toml

@@ -25,3 +25,4 @@ futures = "0.3.19"
 stretch2 = { git = "https://github.com/DioxusLabs/stretch" }
 smallvec = "1.6"
 fxhash = "0.2"
+anymap = "0.12.1"

+ 6 - 1
packages/tui/src/lib.rs

@@ -6,7 +6,9 @@ use crossterm::{
 };
 use dioxus_core::exports::futures_channel::mpsc::unbounded;
 use dioxus_core::*;
-use dioxus_native_core::real_dom::RealDom;
+use dioxus_native_core::{
+    dioxus_native_core_macro::State, real_dom::RealDom, real_dom_new_api::State,
+};
 use futures::{
     channel::mpsc::{UnboundedReceiver, UnboundedSender},
     pin_mut, StreamExt,
@@ -29,6 +31,9 @@ pub use config::*;
 pub use hooks::*;
 pub use render::*;
 
+#[derive(Debug, Clone, State, Default)]
+struct NodeState {}
+
 #[derive(Clone)]
 pub struct TuiContext {
     tx: UnboundedSender<InputEvent>,