Răsfoiți Sursa

Allow Multiple dependencies for states in the RealDom (native core) (#412)

* WIP multable deps

* WIP: refactor and genralize state macro

* add traversable trait

* update tui to use elementid to index dom
Demonthos 3 ani în urmă
părinte
comite
4a5ae758ee

+ 48 - 39
docs/reference/src/guide/custom_renderer.md

@@ -243,11 +243,11 @@ You've probably noticed that many elements in the `rsx!` macros support on-hover
 
 
 # Native Core
 # Native Core
 
 
-Renderers take a lot of work. If you are creating a renderer in rust, native core provides some utilites to implement a renderer. It provides an abstraction over DomEdits and handles layout for you.
+If you are creating a renderer in rust, native core provides some utilites to implement a renderer. It provides an abstraction over DomEdits and handles layout for you.
 
 
 ## RealDom
 ## RealDom
 
 
-The `RealDom` is a higher level abstraction over updating the Dom. It updates with `DomEdits` and provides a way to lazily update the state of nodes based on what attributes change.
+The `RealDom` is a higher level abstraction over updating the Dom. It updates with `DomEdits` and provides a way to incrementally update the state of nodes based on what attributes change.
 
 
 ### Example
 ### Example
 
 
@@ -267,43 +267,52 @@ cx.render(rsx!{
 
 
 In this tree the color depends on the parent's color. The size depends on the childrens size, the current text, and a text size. The border depends on only the current node.
 In this tree the color depends on the parent's color. The size depends on the childrens size, the current text, and a text size. The border depends on only the current node.
 
 
-```mermaid
-flowchart TB
-    subgraph context
-        text_width(text width)
-    end
-    subgraph div
-        state1(state)-->color1(color)
-        state1(state)-->border1(border)
-        border1-.->text_width
-        linkStyle 2 stroke:#5555ff,stroke-width:4px;
-        state1(state)-->layout_width1(layout width)
-    end
-    subgraph p
-        state2(state)-->color2(color)
-        color2-.->color1(color)
-        linkStyle 5 stroke:#0000ff,stroke-width:4px;
-        state2(state)-->border2(border)
-        border2-.->text_width
-        linkStyle 7 stroke:#5555ff,stroke-width:4px;
-        state2(state)-->layout_width2(layout width)
-        layout_width1-.->layout_width2
-        linkStyle 9 stroke:#aaaaff,stroke-width:4px;
-    end
-    subgraph hello world
-        state3(state)-->color3(color)
-        color3-.->color2(color)
-        linkStyle 11 stroke:#0000ff,stroke-width:4px;
-        state3(state)-->border3(border)
-        border3-.->text_width
-        linkStyle 13 stroke:#5555ff,stroke-width:4px;
-        state3(state)-->layout_width3(layout width)
-        layout_width2-.->layout_width3
-        linkStyle 15 stroke:#aaaaff,stroke-width:4px;
-    end
-```
-
-To help in building a Dom, native core provides four traits: State, ChildDepState, ParentDepState, and NodeDepState and a RealDom struct.
+In the following diagram arrows represent dataflow:
+
+[![](https://mermaid.ink/img/pako:eNqdVNFqgzAU_RXJXizUUZPJmIM-jO0LukdhpCbO0JhIGteW0n9fNK1Oa0brfUnu9VxyzzkXjyCVhIIYZFzu0hwr7X2-JcIzsa3W3wqXuZdKoele22oddfa1Y0Tnfn31muvMfqeCDNq3GmvaNROmaKqZFO1DPTRhP8MOd1fTWYNDvzlmQbBMJZcq9JtjNgY1mLVUhBqQPQeojl3wGCw5PsjqnIe-zXqEL8GZ2Kz0gVMPmoeU3ND4IcuiaLGY2zRouuKncv_qGKv3VodpJe0JVU6QCQ5kgqMyWQVr8hbk4hm1PBcmsuwmnrCVH94rP7xN_ucp8sOB_EPSfz9drYVrkpc_AmH8_yTjJueUc-ntpOJkgt2os9tKjcYlt-DLUiD3UsB2KZCLcwjv3Aq33-g2v0M0xXA0MBy5DUdXi-gcJZriuLmAOSioKjAj5ld8rMsJ0DktaAJicyVYbRKQiJPBVSUx438QpqUCcYb5ls4BrrRcHUTaFizqnWGzR8W5evoFI-bJdw)](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqdVNFqgzAU_RXJXizUUZPJmIM-jO0LukdhpCbO0JhIGteW0n9fNK1Oa0brfUnu9VxyzzkXjyCVhIIYZFzu0hwr7X2-JcIzsa3W3wqXuZdKoele22oddfa1Y0Tnfn31muvMfqeCDNq3GmvaNROmaKqZFO1DPTRhP8MOd1fTWYNDvzlmQbBMJZcq9JtjNgY1mLVUhBqQPQeojl3wGCw5PsjqnIe-zXqEL8GZ2Kz0gVMPmoeU3ND4IcuiaLGY2zRouuKncv_qGKv3VodpJe0JVU6QCQ5kgqMyWQVr8hbk4hm1PBcmsuwmnrCVH94rP7xN_ucp8sOB_EPSfz9drYVrkpc_AmH8_yTjJueUc-ntpOJkgt2os9tKjcYlt-DLUiD3UsB2KZCLcwjv3Aq33-g2v0M0xXA0MBy5DUdXi-gcJZriuLmAOSioKjAj5ld8rMsJ0DktaAJicyVYbRKQiJPBVSUx438QpqUCcYb5ls4BrrRcHUTaFizqnWGzR8W5evoFI-bJdw)
+
+[//]: # "%% mermaid flow chart"
+[//]: # "flowchart TB"
+[//]: # "    subgraph context"
+[//]: # "        text_width(text width)"
+[//]: # "    end"
+[//]: # "    subgraph state"
+[//]: # "        direction TB"
+[//]: # "        subgraph div state"
+[//]: # "            direction TB"
+[//]: # "            state1(state)-->color1(color)"
+[//]: # "            state1-->border1(border)"
+[//]: # "            text_width-.->layout_width1(layout width)"
+[//]: # "            linkStyle 2 stroke:#ff5500,stroke-width:4px;"
+[//]: # "            state1-->layout_width1"
+[//]: # "        end"
+[//]: # "        subgraph p state"
+[//]: # "            direction TB"
+[//]: # "            state2(state)-->color2(color)"
+[//]: # "            color1-.->color2"
+[//]: # "            linkStyle 5 stroke:#0000ff,stroke-width:4px;"
+[//]: # "            state2-->border2(border)"
+[//]: # "            text_width-.->layout_width2(layout width)"
+[//]: # "            linkStyle 7 stroke:#ff5500,stroke-width:4px;"
+[//]: # "            state2-->layout_width2"
+[//]: # "            layout_width2-.->layout_width1"
+[//]: # "            linkStyle 9 stroke:#00aa00,stroke-width:4px;"
+[//]: # "        end"
+[//]: # "        subgraph hello world state"
+[//]: # "            direction TB"
+[//]: # "            state3(state)-->border3(border)"
+[//]: # "            state3-->color3(color)"
+[//]: # "            color2-.->color3"
+[//]: # "            linkStyle 12 stroke:#0000ff,stroke-width:4px;"
+[//]: # "            text_width-.->layout_width3(layout width)"
+[//]: # "            linkStyle 13 stroke:#ff5500,stroke-width:4px;"
+[//]: # "            state3-->layout_width3"
+[//]: # "            layout_width3-.->layout_width2"
+[//]: # "            linkStyle 15 stroke:#00aa00,stroke-width:4px;"
+[//]: # "        end"
+[//]: # "    end"
+
+To help in building a Dom, native core provides four traits: State, ChildDepState, ParentDepState, and NodeDepState and a RealDom struct. The ChildDepState, ParentDepState, and NodeDepState provide a way to discribe how some information in a node relates to that of its relatives. By providing how to build a single node from its relations, native-core will derive a way to update the state of all nodes for you with ```#[derive(State)]```. Once you have a state you can provide it as a generic to RealDom. RealDom provides all of the methods to interact and update your new dom.
 
 
 ```rust
 ```rust
 use dioxus_native_core::node_ref::*;
 use dioxus_native_core::node_ref::*;

+ 3 - 4
packages/core-macro/src/props/mod.rs

@@ -83,7 +83,7 @@ mod util {
     }
     }
 
 
     pub fn expr_to_single_string(expr: &syn::Expr) -> Option<String> {
     pub fn expr_to_single_string(expr: &syn::Expr) -> Option<String> {
-        if let syn::Expr::Path(path) = &*expr {
+        if let syn::Expr::Path(path) = expr {
             path_to_single_string(&path.path)
             path_to_single_string(&path.path)
         } else {
         } else {
             None
             None
@@ -779,13 +779,12 @@ Finally, call `.build()` to create the instance of `{name}`.
             // NOTE: both auto_into and strip_option affect `arg_type` and `arg_expr`, but the order of
             // NOTE: both auto_into and strip_option affect `arg_type` and `arg_expr`, but the order of
             // nesting is different so we have to do this little dance.
             // nesting is different so we have to do this little dance.
             let arg_type = if field.builder_attr.strip_option {
             let arg_type = if field.builder_attr.strip_option {
-                let internal_type = field.type_from_inside_option(false).ok_or_else(|| {
+                field.type_from_inside_option(false).ok_or_else(|| {
                     Error::new_spanned(
                     Error::new_spanned(
                         &field_type,
                         &field_type,
                         "can't `strip_option` - field is not `Option<...>`",
                         "can't `strip_option` - field is not `Option<...>`",
                     )
                     )
-                })?;
-                internal_type
+                })?
             } else {
             } else {
                 field_type
                 field_type
             };
             };

+ 1 - 1
packages/html/src/events.rs

@@ -937,7 +937,7 @@ pub mod on {
     feature = "serialize",
     feature = "serialize",
     derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
     derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
 )]
 )]
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 #[repr(u8)]
 #[repr(u8)]
 pub enum KeyCode {
 pub enum KeyCode {
     // That key has no keycode, = 0
     // That key has no keycode, = 0

+ 345 - 460
packages/native-core-macro/src/lib.rs

@@ -2,11 +2,12 @@ extern crate proc_macro;
 
 
 mod sorted_slice;
 mod sorted_slice;
 
 
-use dioxus_native_core::state::MemberId;
 use proc_macro::TokenStream;
 use proc_macro::TokenStream;
-use quote::format_ident;
-use quote::{quote, ToTokens, __private::Span};
+use quote::{quote, ToTokens};
 use sorted_slice::StrSlice;
 use sorted_slice::StrSlice;
+use syn::parenthesized;
+use syn::parse::ParseBuffer;
+use syn::punctuated::Punctuated;
 use syn::{
 use syn::{
     self,
     self,
     parse::{Parse, ParseStream, Result},
     parse::{Parse, ParseStream, Result},
@@ -51,228 +52,99 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
     let strct = Struct::new(type_name.clone(), &fields);
     let strct = Struct::new(type_name.clone(), &fields);
     match StateStruct::parse(&fields, &strct) {
     match StateStruct::parse(&fields, &strct) {
         Ok(state_strct) => {
         Ok(state_strct) => {
-            let node_dep_state_fields = state_strct
+            let members: Vec<_> = state_strct
                 .state_members
                 .state_members
                 .iter()
                 .iter()
-                .filter(|f| f.dep_kind == DepKind::Node)
-                .map(|f| f.reduce_self());
-            let child_dep_state_fields = state_strct
-                .state_members
-                .iter()
-                .filter(|f| f.dep_kind == DepKind::Child)
-                .map(|f| f.reduce_self());
-            let parent_dep_state_fields = state_strct
-                .state_members
-                .iter()
-                .filter(|f| f.dep_kind == DepKind::Parent)
-                .map(|f| f.reduce_self());
-
-            let node_iter = state_strct
-                .state_members
-                .iter()
-                .filter(|m| m.dep_kind == DepKind::Node);
-            let node_ids = node_iter.clone().map(|m| m.member_id.0);
-            let node_ids_clone = node_ids.clone();
-            let node_types = node_iter.map(|f| &f.mem.ty);
-
-            let child_iter = state_strct
-                .state_members
-                .iter()
-                .filter(|m| m.dep_kind == DepKind::Child);
-            let child_ids = child_iter.clone().map(|m| m.member_id.0);
-            let child_ids_clone = child_ids.clone();
-            let child_types = child_iter.map(|f| &f.mem.ty);
-
-            let parent_iter = state_strct
+                .map(|m| &m.mem.ident)
+                .collect();
+            let member_types = state_strct.state_members.iter().map(|m| &m.mem.ty);
+            let resolve_members = state_strct
                 .state_members
                 .state_members
                 .iter()
                 .iter()
-                .filter(|m| m.dep_kind == DepKind::Parent);
-            let parent_ids = parent_iter.clone().map(|m| m.member_id.0);
-            let parent_ids_clone = parent_ids.clone();
-            let parent_types = parent_iter.map(|f| &f.mem.ty);
-
-            let type_name_str = type_name.to_string();
-
-            let child_states = &state_strct.child_states;
+                .map(|m| state_strct.resolve(m));
 
 
-            let member_size = state_strct.state_members.len();
-
-            let child_state_ty = child_states.iter().map(|m| &m.ty);
-            let child_state_idents: Vec<_> = child_states.iter().map(|m| &m.ident).collect();
-            let sum_const_declarations = child_state_ty.clone().enumerate().map(|(i, ty)| {
-                let ident = format_ident!("__{}_SUM_{}", i, type_name.to_string());
-                let ident_minus = format_ident!("__{}_SUM_{}_minus", i, type_name.to_string());
-                if i == 0 {
-                    quote!(const #ident_minus: usize = #member_size + #ty::SIZE - 1;
-                    const #ident: usize = #member_size + #ty::SIZE;)
-                } else {
-                    let prev_ident = format_ident!("__{}_SUM_{}", i - 1, type_name.to_string());
-                    quote!(const #ident_minus: usize = #prev_ident + #ty::SIZE - 1;
-                    const #ident: usize = #prev_ident + #ty::SIZE;)
-                }
-            });
-            let sum_idents: Vec<_> = std::iter::once(quote!(#member_size))
-                .chain((0..child_states.len()).map(|i| {
-                    let ident = format_ident!("__{}_SUM_{}", i, type_name.to_string());
-                    quote!(#ident)
-                }))
-                .collect();
-
-            let child_state_ranges: Vec<_> = (0..child_state_ty.len())
-                .map(|i| {
-                    let current = format_ident!("__{}_SUM_{}_minus", i, type_name.to_string());
-                    let previous = if i == 0 {
-                        quote!(#member_size)
-                    } else {
-                        let ident = format_ident!("__{}_SUM_{}", i - 1, type_name.to_string());
-                        quote!(#ident)
-                    };
-                    quote!(#previous..=#current)
-                })
-                .collect();
+            let child_types = state_strct.child_states.iter().map(|s| &s.ty);
+            let child_members = state_strct.child_states.iter().map(|s| &s.ident);
 
 
             let gen = quote! {
             let gen = quote! {
-                #(
-                    #sum_const_declarations
-                )*
-                impl State for #type_name{
-                    const SIZE: usize = #member_size #( + #child_state_ty::SIZE)*;
-
-                    fn update_node_dep_state<'a>(
-                        &'a mut self,
-                        ty: dioxus_native_core::state::MemberId,
-                        node: &'a dioxus_core::VNode<'a>,
+                impl State for #type_name {
+                    fn update<'a, T: dioxus_native_core::traversable::Traversable<Node = Self, Id = dioxus_core::ElementId>>(
+                        dirty: &[(dioxus_core::ElementId, dioxus_native_core::node_ref::NodeMask)],
+                        state_tree: &'a mut T,
                         vdom: &'a dioxus_core::VirtualDom,
                         vdom: &'a dioxus_core::VirtualDom,
                         ctx: &anymap::AnyMap,
                         ctx: &anymap::AnyMap,
-                    ) -> Option<dioxus_native_core::state::NodeStatesChanged>{
-                        use dioxus_native_core::state::NodeDepState as _;
-                        use dioxus_native_core::state::State as _;
-                        match ty.0{
-                            #(
-                                #node_ids => #node_dep_state_fields,
-                            )*
-                            #(
-                                #child_state_ranges => {
-                                    self.#child_state_idents.update_node_dep_state(
-                                        ty - #sum_idents,
-                                        node,
-                                        vdom,
-                                        ctx,
-                                    ).map(|mut changed|{
-                                        for id in &mut changed.node_dep{
-                                            *id += #sum_idents;
-                                        }
-                                        changed
-                                    })
-                                }
-                            )*
-                            _ => panic!("{:?} not in {}", ty, #type_name_str),
+                    ) -> fxhash::FxHashSet<dioxus_core::ElementId>{
+                        #[derive(Eq, PartialEq)]
+                        struct HeightOrdering {
+                            height: u16,
+                            id: dioxus_core::ElementId,
                         }
                         }
-                    }
 
 
-                    fn update_parent_dep_state<'a>(
-                        &'a mut self,
-                        ty: dioxus_native_core::state::MemberId,
-                        node: &'a dioxus_core::VNode<'a>,
-                        vdom: &'a dioxus_core::VirtualDom,
-                        parent: Option<&Self>,
-                        ctx: &anymap::AnyMap,
-                    ) -> Option<dioxus_native_core::state::ParentStatesChanged>{
-                        use dioxus_native_core::state::ParentDepState as _;
-                        match ty.0{
-                            #(
-                                #parent_ids => #parent_dep_state_fields,
-                            )*
-                            #(
-                                #child_state_ranges => {
-                                    self.#child_state_idents.update_parent_dep_state(
-                                        ty - #sum_idents,
-                                        node,
-                                        vdom,
-                                        parent.map(|p| &p.#child_state_idents),
-                                        ctx,
-                                    ).map(|mut changed|{
-                                        for id in &mut changed.node_dep{
-                                            *id += #sum_idents;
-                                        }
-                                        for id in &mut changed.parent_dep{
-                                            *id += #sum_idents;
-                                        }
-                                        changed
-                                    })
+                        impl HeightOrdering {
+                            fn new(height: u16, id: dioxus_core::ElementId) -> Self {
+                                HeightOrdering {
+                                    height,
+                                    id,
                                 }
                                 }
-                            )*
-                            _ => panic!("{:?} not in {}", ty, #type_name_str),
+                            }
                         }
                         }
-                    }
 
 
-                    fn update_child_dep_state<'a>(
-                        &'a mut self,
-                        ty: dioxus_native_core::state::MemberId,
-                        node: &'a dioxus_core::VNode<'a>,
-                        vdom: &'a dioxus_core::VirtualDom,
-                        children: &[&Self],
-                        ctx: &anymap::AnyMap,
-                    ) -> Option<dioxus_native_core::state::ChildStatesChanged>{
-                        use dioxus_native_core::state::ChildDepState as _;
-                        match ty.0{
-                            #(
-                                #child_ids => #child_dep_state_fields,
-                            )*
-                            #(
-                                #child_state_ranges => {
-                                    self.#child_state_idents.update_child_dep_state(
-                                        ty - #sum_idents,
-                                        node,
-                                        vdom,
-                                        &children.iter().map(|p| &p.#child_state_idents).collect::<Vec<_>>(),
-                                        ctx,
-                                    ).map(|mut changed|{
-                                        for id in &mut changed.node_dep{
-                                            *id += #sum_idents;
-                                        }
-                                        for id in &mut changed.child_dep{
-                                            *id += #sum_idents;
-                                        }
-                                        changed
-                                    })
-                                }
-                            )*
-                            _ => panic!("{:?} not in {}", ty, #type_name_str),
+                        impl Ord for HeightOrdering {
+                            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+                                self.height.cmp(&other.height).then(self.id.0.cmp(&other.id.0))
+                            }
                         }
                         }
-                    }
 
 
-                    fn child_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
-                        let mut dep_types = Vec::new();
-                        #(if #child_types::NODE_MASK.overlaps(mask) {
-                            dep_types.push(dioxus_native_core::state::MemberId(#child_ids_clone));
-                        })*
-                        #(
-                            dep_types.extend(self.#child_state_idents.child_dep_types(mask).into_iter().map(|id| id + #sum_idents));
-                        )*
-                        dep_types
-                    }
+                        impl PartialOrd for HeightOrdering {
+                            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+                                Some(self.cmp(&other))
+                            }
+                        }
+
+                        struct MembersDirty {
+                            #(#members: bool, )*
+                        }
+
+                        impl MembersDirty {
+                            fn new() -> Self {
+                                Self {#(#members: false),*}
+                            }
+
+                            fn any(&self) -> bool {
+                                #(self.#members || )* false
+                            }
+                        }
+
+                        let mut dirty_elements = fxhash::FxHashSet::default();
+                        // the states of any elements that are dirty
+                        let mut states = fxhash::FxHashMap::default();
+
+                        for (id, mask) in dirty {
+                            let members_dirty = MembersDirty {
+                                #(#members: #member_types::NODE_MASK.overlaps(mask),)*
+                            };
+                            if members_dirty.any(){
+                                states.insert(*id, members_dirty);
+                            }
+                            dirty_elements.insert(*id);
+                        }
 
 
-                    fn parent_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
-                        let mut dep_types = Vec::new();
-                        #(if #parent_types::NODE_MASK.overlaps(mask) {
-                            dep_types.push(dioxus_native_core::state::MemberId(#parent_ids_clone));
-                        })*
                         #(
                         #(
-                            dep_types.extend(self.#child_state_idents.parent_dep_types(mask).into_iter().map(|id| id + #sum_idents));
+                            #resolve_members;
                         )*
                         )*
-                        dep_types
-                    }
 
 
-                    fn node_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
-                        let mut dep_types = Vec::new();
-                        #(if #node_types::NODE_MASK.overlaps(mask) {
-                            dep_types.push(dioxus_native_core::state::MemberId(#node_ids_clone));
-                        })*
                         #(
                         #(
-                            dep_types.extend(self.#child_state_idents.node_dep_types(mask).into_iter().map(|id| id + #sum_idents));
+                            dirty_elements.extend(
+                                <#child_types as dioxus_native_core::state::State>::update(
+                                    dirty,
+                                    &mut state_tree.map(|n| &n.#child_members, |n| &mut n.#child_members),
+                                    vdom,
+                                    ctx,
+                                )
+                            );
                         )*
                         )*
-                        dep_types
+
+                        dirty_elements
                     }
                     }
                 }
                 }
             };
             };
@@ -282,6 +154,12 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
     }
     }
 }
 }
 
 
+struct Depenadants<'a> {
+    node: Vec<&'a Member>,
+    child: Vec<&'a Member>,
+    parent: Vec<&'a Member>,
+}
+
 struct Struct {
 struct Struct {
     name: Ident,
     name: Ident,
     members: Vec<Member>,
     members: Vec<Member>,
@@ -302,7 +180,7 @@ struct StateStruct<'a> {
 impl<'a> StateStruct<'a> {
 impl<'a> StateStruct<'a> {
     fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result<Self> {
     fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result<Self> {
         let mut parse_err = Ok(());
         let mut parse_err = Ok(());
-        let state_members = strct
+        let mut unordered_state_members: Vec<_> = strct
             .members
             .members
             .iter()
             .iter()
             .zip(fields.iter())
             .zip(fields.iter())
@@ -312,7 +190,44 @@ impl<'a> StateStruct<'a> {
                     parse_err = Err(err);
                     parse_err = Err(err);
                     None
                     None
                 }
                 }
-            });
+            })
+            .collect();
+        parse_err?;
+
+        let mut state_members = Vec::new();
+        while !unordered_state_members.is_empty() {
+            let mut resolved = false;
+            for i in 0..unordered_state_members.len() {
+                let mem = &mut unordered_state_members[i];
+                if mem.dep_mems.iter().all(|(dep, resolved)| {
+                    *resolved || (*dep == mem.mem && mem.dep_kind != DepKind::Node)
+                }) {
+                    let mem = unordered_state_members.remove(i);
+                    // mark any dependancy that depends on this member as resolved
+                    for member in unordered_state_members.iter_mut() {
+                        for (dep, resolved) in &mut member.dep_mems {
+                            *resolved |= *dep == mem.mem;
+                        }
+                    }
+                    state_members.push(mem);
+                    resolved = true;
+                    break;
+                }
+            }
+            if !resolved {
+                return Err(Error::new(
+                    strct.name.span(),
+                    format!(
+                        "{} has circular dependacy in {:?}",
+                        strct.name,
+                        unordered_state_members
+                            .iter()
+                            .map(|m| format!("{}", &m.mem.ident))
+                            .collect::<Vec<_>>()
+                    ),
+                ));
+            }
+        }
 
 
         let child_states = strct
         let child_states = strct
             .members
             .members
@@ -328,177 +243,234 @@ impl<'a> StateStruct<'a> {
             })
             })
             .map(|(m, _)| m);
             .map(|(m, _)| m);
 
 
-        #[derive(Debug, Clone)]
-        struct DepNode<'a> {
-            state_mem: StateMember<'a>,
-            depandants: Vec<DepNode<'a>>,
-        }
-        impl<'a> DepNode<'a> {
-            fn new(state_mem: StateMember<'a>) -> Self {
-                Self {
-                    state_mem,
-                    depandants: Vec::new(),
-                }
-            }
+        // members need to be sorted so that members are updated after the members they depend on
+        Ok(Self {
+            state_members,
+            child_states: child_states.collect(),
+        })
+    }
 
 
-            /// flattens the node in pre order
-            fn flatten(self) -> Vec<StateMember<'a>> {
-                let DepNode {
-                    state_mem,
-                    depandants,
-                } = self;
-                let mut flat = vec![state_mem];
-                for d in depandants {
-                    flat.append(&mut d.flatten());
+    fn get_depenadants(&self, mem: &Member) -> Depenadants {
+        let mut dependants = Depenadants {
+            node: Vec::new(),
+            child: Vec::new(),
+            parent: Vec::new(),
+        };
+        for member in &self.state_members {
+            for (dep, _) in &member.dep_mems {
+                if *dep == mem {
+                    match member.dep_kind {
+                        DepKind::Node => dependants.node.push(member.mem),
+                        DepKind::Child => dependants.child.push(member.mem),
+                        DepKind::Parent => dependants.parent.push(member.mem),
+                    }
                 }
                 }
-                flat
             }
             }
+        }
+        dependants
+    }
 
 
-            fn set_ids(&mut self, current_id: &mut usize) {
-                self.state_mem.member_id = dioxus_native_core::state::MemberId(*current_id);
-                // if the node depends on itself, we need to add the dependency seperately
-                if let Some(dep) = self.state_mem.dep_mem {
-                    if dep == self.state_mem.mem {
-                        self.state_mem
-                            .dependants
-                            .push((MemberId(*current_id), self.state_mem.dep_kind.clone()));
+    fn update_dependants(&self, mem: &Member) -> impl ToTokens {
+        let dep = self.get_depenadants(mem);
+        let update_child_dependants = if dep.child.is_empty() {
+            quote!()
+        } else {
+            let insert = dep.child.iter().map(|d|{
+                if *d == mem {
+                    quote! {
+                        let seeking = HeightOrdering::new(state_tree.height(parent_id).unwrap(), parent_id);
+                        if let Err(idx) = resolution_order
+                            .binary_search_by(|ordering| ordering.cmp(&seeking).reverse()){
+                            resolution_order.insert(
+                                idx,
+                                seeking,
+                            );
+                        }
                     }
                     }
+                } else {
+                    quote! {}
                 }
                 }
-                *current_id += 1;
-                for d in &mut self.depandants {
-                    self.state_mem
-                        .dependants
-                        .push((MemberId(*current_id), d.state_mem.dep_kind.clone()));
-                    d.set_ids(current_id);
+            });
+            let update: Vec<_> = dep
+                .child
+                .iter()
+                .map(|d| {
+                    let ident = &d.ident;
+                    quote! {
+                        dirty.#ident = true;
+                    }
+                })
+                .collect();
+            quote! {
+                if let Some(parent_id) = state_tree.parent(id) {
+                    #(#insert)*
+                    if let Some(dirty) = states.get_mut(&parent_id) {
+                        #(#update)*
+                    }
+                    else {
+                        let mut dirty = MembersDirty::new();
+                        #(#update)*
+                        states.insert(parent_id, dirty);
+                    }
                 }
                 }
             }
             }
-
-            fn contains_member(&self, member: &Member) -> bool {
-                if self.state_mem.mem == member {
-                    true
+        };
+        let node_dependants: Vec<_> = dep.node.iter().map(|d| &d.ident).collect();
+        let update_node_dependants = quote! {#(members_dirty.#node_dependants = true;)*};
+        let update_parent_dependants = if dep.parent.is_empty() {
+            quote!()
+        } else {
+            let insert = dep.parent.iter().map(|d| {
+                if *d == mem {
+                    quote! {
+                        let seeking = HeightOrdering::new(state_tree.height(*child_id).unwrap(), *child_id);
+                        if let Err(idx) = resolution_order
+                            .binary_search(&seeking){
+                            resolution_order.insert(
+                                idx,
+                                seeking,
+                            );
+                        }
+                    }
                 } else {
                 } else {
-                    self.depandants.iter().any(|d| d.contains_member(member))
+                    quote! {}
+                }
+            });
+            let update: Vec<_> = dep
+                .parent
+                .iter()
+                .map(|d| {
+                    let ident = &d.ident;
+                    quote! {
+                        dirty.#ident = true;
+                    }
+                })
+                .collect();
+            quote! {
+                for child_id in state_tree.children(id) {
+                    #(#insert)*
+                    if let Some(dirty) = states.get_mut(&child_id) {
+                        #(#update)*
+                    }
+                    else {
+                        let mut dirty = MembersDirty::new();
+                        #(#update)*
+                        states.insert(*child_id, dirty);
+                    }
                 }
                 }
             }
             }
+        };
 
 
-            // check if there are any mixed child/parent dependancies
-            fn check(&self) -> Option<Error> {
-                self.kind().err()
-            }
+        quote! {
+            #update_node_dependants
+            #update_child_dependants
+            #update_parent_dependants
+        }
+    }
 
 
-            fn kind(&self) -> Result<&DepKind> {
-                fn reduce_kind<'a>(dk1: &'a DepKind, dk2: &'a DepKind) -> Result<&'a DepKind> {
-                    match (dk1, dk2) {
-                        (DepKind::Child, DepKind::Parent) | (DepKind::Parent, DepKind::Child) => {
-                            Err(Error::new(
-                                Span::call_site(),
-                                "There is a ChildDepState that depends on a ParentDepState",
-                            ))
+    fn resolve(&self, mem: &StateMember) -> impl ToTokens {
+        let reduce_member = mem.reduce_self();
+        let update_dependant = self.update_dependants(mem.mem);
+        let member = &mem.mem.ident;
+
+        match mem.dep_kind {
+            DepKind::Parent => {
+                quote! {
+                    // resolve parent dependant state
+                    let mut resolution_order = states.keys().copied().map(|id| HeightOrdering::new(state_tree.height(id).unwrap(), id)).collect::<Vec<_>>();
+                    resolution_order.sort();
+                    let mut i = 0;
+                    while i < resolution_order.len(){
+                        let id = resolution_order[i].id;
+                        let vnode = vdom.get_element(id).unwrap();
+                        let members_dirty = states.get_mut(&id).unwrap();
+                        let (current_state, parent) = state_tree.get_node_parent_mut(id);
+                        let current_state = current_state.unwrap();
+                        if members_dirty.#member && #reduce_member {
+                            dirty_elements.insert(id);
+                            #update_dependant
                         }
                         }
-                        // node dep state takes the lowest priority
-                        (DepKind::Node, important) | (important, DepKind::Node) => Ok(important),
-                        // they are the same
-                        (fst, _) => Ok(fst),
+                        i += 1;
                     }
                     }
                 }
                 }
-                reduce_kind(
-                    self.depandants
-                        .iter()
-                        .try_fold(&DepKind::Node, |dk1, dk2| reduce_kind(dk1, dk2.kind()?))?,
-                    &self.state_mem.dep_kind,
-                )
             }
             }
-
-            fn insert_dependant(&mut self, other: DepNode<'a>) -> bool {
-                let dep = other.state_mem.dep_mem.unwrap();
-                if self.contains_member(dep) {
-                    if self.state_mem.mem == dep {
-                        self.depandants.push(other);
-                        true
-                    } else {
-                        self.depandants
-                            .iter_mut()
-                            .find(|d| d.contains_member(dep))
-                            .unwrap()
-                            .insert_dependant(other)
+            DepKind::Child => {
+                quote! {
+                    // resolve child dependant state
+                    let mut resolution_order = states.keys().copied().map(|id| HeightOrdering::new(state_tree.height(id).unwrap(), id)).collect::<Vec<_>>();
+                    resolution_order.sort_by(|height_ordering1, height_ordering2| {
+                        height_ordering1.cmp(&height_ordering2).reverse()
+                    });
+                    let mut i = 0;
+                    while i < resolution_order.len(){
+                        let id = resolution_order[i].id;
+                        let vnode = vdom.get_element(id).unwrap();
+                        let members_dirty = states.get_mut(&id).unwrap();
+                        let (current_state, children) = state_tree.get_node_children_mut(id);
+                        let current_state = current_state.unwrap();
+                        if members_dirty.#member && #reduce_member {
+                            dirty_elements.insert(id);
+                            #update_dependant
+                        }
+                        i += 1;
                     }
                     }
-                } else {
-                    false
                 }
                 }
             }
             }
-        }
-
-        // members need to be sorted so that members are updated after the members they depend on
-        let mut roots: Vec<DepNode> = vec![];
-        for m in state_members {
-            if let Some(dep) = m.dep_mem {
-                let root_depends_on = roots
-                    .iter()
-                    .filter_map(|m| m.state_mem.dep_mem)
-                    .any(|d| m.mem == d);
-
-                if let Some(r) = roots.iter_mut().find(|r| r.contains_member(dep)) {
-                    let new = DepNode::new(m);
-                    if root_depends_on {
-                        return Err(Error::new(
-                            new.state_mem.mem.ident.span(),
-                            format!("{} has a circular dependancy", new.state_mem.mem.ident),
-                        ));
+            DepKind::Node => {
+                quote! {
+                    // resolve node dependant state
+                    let mut resolution_order = states.keys().copied().collect::<Vec<_>>();
+                    let mut i = 0;
+                    while i < resolution_order.len(){
+                        let id = resolution_order[i];
+                        let vnode = vdom.get_element(id).unwrap();
+                        let members_dirty = states.get_mut(&id).unwrap();
+                        let current_state = state_tree.get_mut(id).unwrap();
+                        if members_dirty.#member && #reduce_member {
+                            dirty_elements.insert(id);
+                            #update_dependant
+                        }
+                        i += 1;
                     }
                     }
-                    // return Err(Error::new(new.state_mem.mem.ident.span(), "stuff"));
-                    r.insert_dependant(new);
-                    continue;
-                }
-            }
-            let mut new = DepNode::new(m);
-            let mut i = 0;
-            while i < roots.len() {
-                if roots[i].state_mem.dep_mem == Some(new.state_mem.mem) {
-                    let child = roots.remove(i);
-                    new.insert_dependant(child);
-                } else {
-                    i += 1;
                 }
                 }
             }
             }
-            roots.push(new);
-        }
-        parse_err?;
-        let mut current_id = 0;
-        for r in &mut roots {
-            r.set_ids(&mut current_id);
-        }
-        if let Some(err) = roots.iter().find_map(DepNode::check) {
-            Err(err)
-        } else {
-            let state_members: Vec<_> = roots
-                .into_iter()
-                .flat_map(|r| r.flatten().into_iter())
-                .collect();
-
-            Ok(Self {
-                state_members,
-                child_states: child_states.collect(),
-            })
         }
         }
     }
     }
 }
 }
 
 
+fn try_parenthesized(input: ParseStream) -> Result<ParseBuffer> {
+    let inside;
+    parenthesized!(inside in input);
+    Ok(inside)
+}
+
 struct Dependancy {
 struct Dependancy {
     ctx_ty: Option<Type>,
     ctx_ty: Option<Type>,
-    dep: Option<Ident>,
+    deps: Vec<Ident>,
 }
 }
 
 
 impl Parse for Dependancy {
 impl Parse for Dependancy {
     fn parse(input: ParseStream) -> Result<Self> {
     fn parse(input: ParseStream) -> Result<Self> {
-        let dep = input
-            .parse()
-            .ok()
-            .filter(|i: &Ident| format!("{}", i) != "NONE");
+        let deps: Option<Punctuated<Ident, Token![,]>> = {
+            try_parenthesized(input)
+                .ok()
+                .and_then(|inside| inside.parse_terminated(Ident::parse).ok())
+        };
+        let deps: Vec<_> = deps
+            .map(|deps| deps.into_iter().collect())
+            .or_else(|| {
+                input
+                    .parse::<Ident>()
+                    .ok()
+                    .filter(|i: &Ident| format!("{}", i) != "NONE")
+                    .map(|i| vec![i])
+            })
+            .unwrap_or_default();
         let comma: Option<Token![,]> = input.parse().ok();
         let comma: Option<Token![,]> = input.parse().ok();
         let ctx_ty = input.parse().ok();
         let ctx_ty = input.parse().ok();
         Ok(Self {
         Ok(Self {
             ctx_ty: comma.and(ctx_ty),
             ctx_ty: comma.and(ctx_ty),
-            dep,
+            deps,
         })
         })
     }
     }
 }
 }
@@ -522,11 +494,9 @@ impl Member {
 struct StateMember<'a> {
 struct StateMember<'a> {
     mem: &'a Member,
     mem: &'a Member,
     dep_kind: DepKind,
     dep_kind: DepKind,
-    dep_mem: Option<&'a Member>,
+    // the depenancy and if it is satified
+    dep_mems: Vec<(&'a Member, bool)>,
     ctx_ty: Option<Type>,
     ctx_ty: Option<Type>,
-    dependants: Vec<(dioxus_native_core::state::MemberId, DepKind)>,
-    // This is just the index of the final order of the struct it is used to communicate which parts need updated and what order to update them in.
-    member_id: dioxus_native_core::state::MemberId,
 }
 }
 
 
 impl<'a> StateMember<'a> {
 impl<'a> StateMember<'a> {
@@ -548,26 +518,26 @@ impl<'a> StateMember<'a> {
                 })?;
                 })?;
             match a.parse_args::<Dependancy>() {
             match a.parse_args::<Dependancy>() {
                 Ok(dependancy) => {
                 Ok(dependancy) => {
-                    let dep_mem = if let Some(name) = &dependancy.dep {
-                        if let Some(found) = parent.members.iter().find(|m| &m.ident == name) {
-                            Some(found)
-                        } else {
-                            err = Err(Error::new(
-                                name.span(),
-                                format!("{} not found in {}", name, parent.name),
-                            ));
-                            None
-                        }
-                    } else {
-                        None
-                    };
+                    let dep_mems = dependancy
+                        .deps
+                        .iter()
+                        .filter_map(|name| {
+                            if let Some(found) = parent.members.iter().find(|m| &m.ident == name) {
+                                Some((found, false))
+                            } else {
+                                err = Err(Error::new(
+                                    name.span(),
+                                    format!("{} not found in {}", name, parent.name),
+                                ));
+                                None
+                            }
+                        })
+                        .collect();
                     Some(Self {
                     Some(Self {
                         mem,
                         mem,
                         dep_kind,
                         dep_kind,
-                        dep_mem,
+                        dep_mems,
                         ctx_ty: dependancy.ctx_ty,
                         ctx_ty: dependancy.ctx_ty,
-                        dependants: Vec::new(),
-                        member_id: dioxus_native_core::state::MemberId(0),
                     })
                     })
                 }
                 }
                 Err(e) => {
                 Err(e) => {
@@ -592,111 +562,26 @@ impl<'a> StateMember<'a> {
         } else {
         } else {
             quote! {&()}
             quote! {&()}
         };
         };
-        let states_changed = {
-            let child_dep = self
-                .dependants
-                .iter()
-                .filter(|(_, kind)| kind == &DepKind::Child)
-                .map(|(id, _)| id.0);
-            let parent_dep = self
-                .dependants
-                .iter()
-                .filter(|(_, kind)| kind == &DepKind::Parent)
-                .map(|(id, _)| id.0);
-            let node_dep = self
-                .dependants
-                .iter()
-                .filter(|(_, kind)| kind == &DepKind::Node)
-                .map(|(id, _)| id.0);
-            match self.dep_kind {
-                DepKind::Node => {
-                    quote! {
-                        dioxus_native_core::state::NodeStatesChanged{
-                            node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
-                        }
-                    }
-                }
-                DepKind::Child => {
-                    quote! {
-                        dioxus_native_core::state::ChildStatesChanged{
-                            node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
-                            child_dep: vec![#(dioxus_native_core::state::MemberId(#child_dep), )*],
-                        }
-                    }
-                }
-                DepKind::Parent => {
-                    quote! {
-                        dioxus_native_core::state::ParentStatesChanged{
-                            node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
-                            parent_dep: vec![#(dioxus_native_core::state::MemberId(#parent_dep), )*],
-                        }
-                    }
-                }
-            }
-        };
 
 
         let ty = &self.mem.ty;
         let ty = &self.mem.ty;
         let node_view =
         let node_view =
-            quote!(dioxus_native_core::node_ref::NodeView::new(node, #ty::NODE_MASK, vdom));
-        if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
-            match self.dep_kind {
-                DepKind::Node => {
-                    quote!({
-                        if self.#ident.reduce(#node_view, &self.#dep_ident, #get_ctx){
-                            Some(#states_changed)
-                        } else{
-                            None
-                        }
-                    })
-                }
-                DepKind::Child => {
-                    quote!({
-                        if self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx){
-                            Some(#states_changed)
-                        } else{
-                            None
-                        }
-                    })
-                }
-                DepKind::Parent => {
-                    quote!({
-                        if self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx){
-                            Some(#states_changed)
-                        } else{
-                            None
-                        }
-                    })
-                }
+            quote!(dioxus_native_core::node_ref::NodeView::new(vnode, #ty::NODE_MASK, vdom));
+        let dep_idents = self.dep_mems.iter().map(|m| &m.0.ident);
+        match self.dep_kind {
+            DepKind::Node => {
+                quote!({
+                    current_state.#ident.reduce(#node_view, (#(&current_state.#dep_idents,)*), #get_ctx)
+                })
             }
             }
-        } else {
-            match self.dep_kind {
-                DepKind::Node => {
-                    quote!({
-                        if self.#ident.reduce(#node_view, &(), #get_ctx){
-                            Some(#states_changed)
-                        } else{
-                            None
-                        }
-                    })
-                }
-                DepKind::Child => {
-                    quote!({
-                        if self.#ident.reduce(#node_view, std::iter::empty(), #get_ctx){
-                            Some(#states_changed)
-                        } else{
-                            None
-                        }
-                    })
-                }
-                DepKind::Parent => {
-                    quote!({
-                        if self.#ident.reduce(#node_view, Some(&()), #get_ctx){
-                            Some(#states_changed)
-                        } else{
-                            None
-                        }
-                    })
-                }
+            DepKind::Child => {
+                quote!({
+                    current_state.#ident.reduce(#node_view, children.iter().map(|c| (#(&c.#dep_idents)*)), #get_ctx)
+                })
+            }
+            DepKind::Parent => {
+                quote!({
+                    current_state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.#dep_idents)*)), #get_ctx)
+                })
             }
             }
         }
         }
     }
     }

+ 256 - 0
packages/native-core-macro/tests/called_minimally_on_build.rs

@@ -0,0 +1,256 @@
+use anymap::AnyMap;
+use dioxus::core as dioxus_core;
+use dioxus::prelude::*;
+use dioxus_native_core::node_ref::*;
+use dioxus_native_core::real_dom::*;
+use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
+use dioxus_native_core_macro::State;
+
+macro_rules! dep {
+    ( child( $name:ty, $dep:ty ) ) => {
+        impl ChildDepState for $name {
+            type Ctx = ();
+            type DepState = $dep;
+            const NODE_MASK: NodeMask = NodeMask::ALL;
+            fn reduce<'a>(
+                &mut self,
+                _: NodeView,
+                _: impl Iterator<Item = &'a Self::DepState>,
+                _: &Self::Ctx,
+            ) -> bool
+            where
+                Self::DepState: 'a,
+            {
+                self.0 += 1;
+                true
+            }
+        }
+    };
+
+    ( parent( $name:ty, $dep:ty ) ) => {
+        impl ParentDepState for $name {
+            type Ctx = ();
+            type DepState = $dep;
+            const NODE_MASK: NodeMask = NodeMask::ALL;
+            fn reduce(
+                &mut self,
+                _: NodeView,
+                _: Option<&Self::DepState>,
+                _: &Self::Ctx,
+            ) -> bool {
+                self.0 += 1;
+                true
+            }
+        }
+    };
+
+    ( node( $name:ty, ($($l:lifetime),*), $dep:ty ) ) => {
+        impl<$($l),*> NodeDepState<$dep> for $name {
+            type Ctx = ();
+            const NODE_MASK: NodeMask = NodeMask::ALL;
+            fn reduce(
+                &mut self,
+                _: NodeView,
+                _: $dep,
+                _: &Self::Ctx,
+            ) -> bool {
+                self.0 += 1;
+                true
+            }
+        }
+    };
+}
+
+macro_rules! test_state{
+    ( $s:ty, child: ( $( $child:ident ),* ), node: ( $( $node:ident ),* ), parent: ( $( $parent:ident ),* ) ) => {
+        #[test]
+        fn state_reduce_initally_called_minimally() {
+            #[allow(non_snake_case)]
+            fn Base(cx: Scope) -> Element {
+                rsx!(cx, div {
+                    div{
+                        div{
+                            p{}
+                        }
+                        p{
+                            "hello"
+                        }
+                        div{
+                            h1{}
+                        }
+                        p{
+                            "world"
+                        }
+                    }
+                })
+            }
+
+            let vdom = VirtualDom::new(Base);
+
+            let mutations = vdom.create_vnodes(rsx! {
+                div {
+                    div{
+                        div{
+                            p{}
+                        }
+                        p{
+                            "hello"
+                        }
+                        div{
+                            h1{}
+                        }
+                        p{
+                            "world"
+                        }
+                    }
+                }
+            });
+
+            let mut dom: RealDom<$s> = RealDom::new();
+
+            let nodes_updated = dom.apply_mutations(vec![mutations]);
+            let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
+
+            dom.traverse_depth_first(|n| {
+                $(
+                    assert_eq!(n.state.$child.0, 1);
+                )*
+                $(
+                    assert_eq!(n.state.$node.0, 1);
+                )*
+                $(
+                    assert_eq!(n.state.$parent.0, 1);
+                )*
+            });
+        }
+    }
+}
+
+mod node_depends_on_child_and_parent {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Node(i32);
+    dep!(node(Node,  ('a, 'b), (&'a Child, &'b Parent)));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Child(i32);
+    dep!(child(Child, Child));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Parent(i32);
+    dep!(parent(Parent, Parent));
+
+    #[derive(Debug, Clone, Default, State)]
+    struct StateTester {
+        #[node_dep_state((child, parent))]
+        node: Node,
+        #[child_dep_state(child)]
+        child: Child,
+        #[parent_dep_state(parent)]
+        parent: Parent,
+    }
+
+    test_state!(StateTester, child: (child), node: (node), parent: (parent));
+}
+
+mod child_depends_on_node_that_depends_on_parent {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Node(i32);
+    dep!(node(Node, ('a), (&'a Parent,)));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Child(i32);
+    dep!(child(Child, Node));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Parent(i32);
+    dep!(parent(Parent, Parent));
+
+    #[derive(Debug, Clone, Default, State)]
+    struct StateTester {
+        #[node_dep_state(parent)]
+        node: Node,
+        #[child_dep_state(node)]
+        child: Child,
+        #[parent_dep_state(parent)]
+        parent: Parent,
+    }
+
+    test_state!(StateTester, child: (child), node: (node), parent: (parent));
+}
+
+mod parent_depends_on_node_that_depends_on_child {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Node(i32);
+    dep!(node(Node, ('a), (&'a Child,)));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Child(i32);
+    dep!(child(Child, Child));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Parent(i32);
+    dep!(parent(Parent, Node));
+
+    #[derive(Debug, Clone, Default, State)]
+    struct StateTester {
+        #[node_dep_state(child)]
+        node: Node,
+        #[child_dep_state(child)]
+        child: Child,
+        #[parent_dep_state(node)]
+        parent: Parent,
+    }
+
+    test_state!(StateTester, child: (child), node: (node), parent: (parent));
+}
+
+mod node_depends_on_other_node_state {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Node1(i32);
+    dep!(node(Node1, ('a), (&'a Node2,)));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Node2(i32);
+    dep!(node(Node2, (), ()));
+
+    #[derive(Debug, Clone, Default, State)]
+    struct StateTester {
+        #[node_dep_state((node2))]
+        node1: Node1,
+        #[node_dep_state()]
+        node2: Node2,
+    }
+
+    test_state!(StateTester, child: (), node: (node1, node2), parent: ());
+}
+
+mod node_child_and_parent_state_depends_on_self {
+    use super::*;
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Node(i32);
+    dep!(node(Node, (), ()));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Child(i32);
+    dep!(child(Child, Child));
+
+    #[derive(Debug, Clone, Default, PartialEq)]
+    struct Parent(i32);
+    dep!(parent(Parent, Parent));
+
+    #[derive(Debug, Clone, Default, State)]
+    struct StateTester {
+        #[node_dep_state()]
+        node: Node,
+        #[child_dep_state(child)]
+        child: Child,
+        #[parent_dep_state(parent)]
+        parent: Parent,
+    }
+
+    test_state!(StateTester, child: (child), node: (node), parent: (parent));
+}

+ 6 - 6
packages/native-core-macro/tests/change_nodes.rs

@@ -51,8 +51,8 @@ fn remove_node() {
 
 
     assert_eq!(dom.size(), 2);
     assert_eq!(dom.size(), 2);
     assert!(&dom.contains_node(&VNode::Element(&root_div)));
     assert!(&dom.contains_node(&VNode::Element(&root_div)));
-    assert_eq!(dom[1].height, 1);
-    assert_eq!(dom[2].height, 2);
+    assert_eq!(dom[ElementId(1)].height, 1);
+    assert_eq!(dom[ElementId(2)].height, 2);
 
 
     let vdom = VirtualDom::new(Base);
     let vdom = VirtualDom::new(Base);
     let mutations = vdom.diff_lazynodes(
     let mutations = vdom.diff_lazynodes(
@@ -80,7 +80,7 @@ fn remove_node() {
 
 
     assert_eq!(dom.size(), 1);
     assert_eq!(dom.size(), 1);
     assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
     assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
-    assert_eq!(dom[1].height, 1);
+    assert_eq!(dom[ElementId(1)].height, 1);
 }
 }
 
 
 #[test]
 #[test]
@@ -113,7 +113,7 @@ fn add_node() {
 
 
     assert_eq!(dom.size(), 1);
     assert_eq!(dom.size(), 1);
     assert!(&dom.contains_node(&VNode::Element(&root_div)));
     assert!(&dom.contains_node(&VNode::Element(&root_div)));
-    assert_eq!(dom[1].height, 1);
+    assert_eq!(dom[ElementId(1)].height, 1);
 
 
     let vdom = VirtualDom::new(Base);
     let vdom = VirtualDom::new(Base);
     let mutations = vdom.diff_lazynodes(
     let mutations = vdom.diff_lazynodes(
@@ -152,6 +152,6 @@ fn add_node() {
 
 
     assert_eq!(dom.size(), 2);
     assert_eq!(dom.size(), 2);
     assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
     assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
-    assert_eq!(dom[1].height, 1);
-    assert_eq!(dom[2].height, 2);
+    assert_eq!(dom[ElementId(1)].height, 1);
+    assert_eq!(dom[ElementId(2)].height, 2);
 }
 }

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

@@ -40,7 +40,7 @@ fn initial_build_simple() {
     };
     };
     assert_eq!(dom.size(), 1);
     assert_eq!(dom.size(), 1);
     assert!(&dom.contains_node(&VNode::Element(&root_div)));
     assert!(&dom.contains_node(&VNode::Element(&root_div)));
-    assert_eq!(dom[1].height, 1);
+    assert_eq!(dom[ElementId(1)].height, 1);
 }
 }
 
 
 #[test]
 #[test]
@@ -119,10 +119,10 @@ fn initial_build_with_children() {
     };
     };
     assert_eq!(dom.size(), 6);
     assert_eq!(dom.size(), 6);
     assert!(&dom.contains_node(&VNode::Element(&root_div)));
     assert!(&dom.contains_node(&VNode::Element(&root_div)));
-    assert_eq!(dom[1].height, 1);
-    assert_eq!(dom[2].height, 2);
-    assert_eq!(dom[3].height, 3);
-    assert_eq!(dom[4].height, 3);
-    assert_eq!(dom[5].height, 4);
-    assert_eq!(dom[6].height, 3);
+    assert_eq!(dom[ElementId(1)].height, 1);
+    assert_eq!(dom[ElementId(2)].height, 2);
+    assert_eq!(dom[ElementId(3)].height, 3);
+    assert_eq!(dom[ElementId(4)].height, 3);
+    assert_eq!(dom[ElementId(5)].height, 4);
+    assert_eq!(dom[ElementId(6)].height, 3);
 }
 }

+ 26 - 77
packages/native-core-macro/tests/update_state.rs

@@ -1,5 +1,6 @@
 use anymap::AnyMap;
 use anymap::AnyMap;
 use dioxus::core as dioxus_core;
 use dioxus::core as dioxus_core;
+use dioxus::core::ElementId;
 use dioxus::core::{AttributeValue, DomEdit, Mutations};
 use dioxus::core::{AttributeValue, DomEdit, Mutations};
 use dioxus::prelude::*;
 use dioxus::prelude::*;
 use dioxus_native_core::node_ref::*;
 use dioxus_native_core::node_ref::*;
@@ -49,13 +50,14 @@ impl ChildDepState for ChildDepCallCounter {
     const NODE_MASK: NodeMask = NodeMask::ALL;
     const NODE_MASK: NodeMask = NodeMask::ALL;
     fn reduce<'a>(
     fn reduce<'a>(
         &mut self,
         &mut self,
-        _: NodeView,
+        node: NodeView,
         _: impl Iterator<Item = &'a Self::DepState>,
         _: impl Iterator<Item = &'a Self::DepState>,
         _: &Self::Ctx,
         _: &Self::Ctx,
     ) -> bool
     ) -> bool
     where
     where
         Self::DepState: 'a,
         Self::DepState: 'a,
     {
     {
+        println!("{self:?} {:?}: {} {:?}", node.tag(), node.id(), node.text());
         self.0 += 1;
         self.0 += 1;
         true
         true
     }
     }
@@ -80,11 +82,10 @@ impl ParentDepState for ParentDepCallCounter {
 
 
 #[derive(Debug, Clone, Default)]
 #[derive(Debug, Clone, Default)]
 struct NodeDepCallCounter(u32);
 struct NodeDepCallCounter(u32);
-impl NodeDepState for NodeDepCallCounter {
+impl NodeDepState<()> for NodeDepCallCounter {
     type Ctx = ();
     type Ctx = ();
-    type DepState = ();
     const NODE_MASK: NodeMask = NodeMask::ALL;
     const NODE_MASK: NodeMask = NodeMask::ALL;
-    fn reduce(&mut self, _node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
+    fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
         self.0 += 1;
         self.0 += 1;
         true
         true
     }
     }
@@ -133,11 +134,10 @@ impl ParentDepState for PushedDownStateTester {
 
 
 #[derive(Debug, Clone, PartialEq, Default)]
 #[derive(Debug, Clone, PartialEq, Default)]
 struct NodeStateTester(Option<String>, Vec<(String, String)>);
 struct NodeStateTester(Option<String>, Vec<(String, String)>);
-impl NodeDepState for NodeStateTester {
+impl NodeDepState<()> for NodeStateTester {
     type Ctx = u32;
     type Ctx = u32;
-    type DepState = ();
     const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag();
     const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag();
-    fn reduce(&mut self, node: NodeView, _sibling: &Self::DepState, ctx: &Self::Ctx) -> bool {
+    fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool {
         assert_eq!(*ctx, 42);
         assert_eq!(*ctx, 42);
         *self = NodeStateTester(
         *self = NodeStateTester(
             node.tag().map(|s| s.to_string()),
             node.tag().map(|s| s.to_string()),
@@ -187,7 +187,7 @@ fn state_initial() {
     ctx.insert(42u32);
     ctx.insert(42u32);
     let _to_rerender = dom.update_state(&vdom, nodes_updated, ctx);
     let _to_rerender = dom.update_state(&vdom, nodes_updated, ctx);
 
 
-    let root_div = &dom[1];
+    let root_div = &dom[ElementId(1)];
     assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
     assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
     assert_eq!(
     assert_eq!(
         root_div.state.bubbled.1,
         root_div.state.bubbled.1,
@@ -204,7 +204,7 @@ fn state_initial() {
     assert_eq!(root_div.state.node.0, Some("div".to_string()));
     assert_eq!(root_div.state.node.0, Some("div".to_string()));
     assert_eq!(root_div.state.node.1, vec![]);
     assert_eq!(root_div.state.node.1, vec![]);
 
 
-    let child_p = &dom[2];
+    let child_p = &dom[ElementId(2)];
     assert_eq!(child_p.state.bubbled.0, Some("p".to_string()));
     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.bubbled.1, Vec::new());
     assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
     assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
@@ -221,7 +221,7 @@ fn state_initial() {
         vec![("color".to_string(), "red".to_string())]
         vec![("color".to_string(), "red".to_string())]
     );
     );
 
 
-    let child_h1 = &dom[3];
+    let child_h1 = &dom[ElementId(3)];
     assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string()));
     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.bubbled.1, Vec::new());
     assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
     assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
@@ -236,64 +236,6 @@ fn state_initial() {
     assert_eq!(child_h1.state.node.1, vec![]);
     assert_eq!(child_h1.state.node.1, vec![]);
 }
 }
 
 
-#[test]
-fn state_reduce_initally_called_minimally() {
-    #[allow(non_snake_case)]
-    fn Base(cx: Scope) -> Element {
-        rsx!(cx, div {
-            div{
-                div{
-                    p{}
-                }
-                p{
-                    "hello"
-                }
-                div{
-                    h1{}
-                }
-                p{
-                    "world"
-                }
-            }
-        })
-    }
-
-    let vdom = VirtualDom::new(Base);
-
-    let mutations = vdom.create_vnodes(rsx! {
-        div {
-            div{
-                div{
-                    p{}
-                }
-                p{
-                    "hello"
-                }
-                div{
-                    h1{}
-                }
-                p{
-                    "world"
-                }
-            }
-        }
-    });
-
-    let mut dom: RealDom<CallCounterState> = RealDom::new();
-
-    let nodes_updated = dom.apply_mutations(vec![mutations]);
-    let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
-
-    dom.traverse_depth_first(|n| {
-        assert_eq!(n.state.part1.child_counter.0, 1);
-        assert_eq!(n.state.child_counter.0, 1);
-        assert_eq!(n.state.part2.parent_counter.0, 1);
-        assert_eq!(n.state.parent_counter.0, 1);
-        assert_eq!(n.state.part3.node_counter.0, 1);
-        assert_eq!(n.state.node_counter.0, 1);
-    });
-}
-
 #[test]
 #[test]
 fn state_reduce_parent_called_minimally_on_update() {
 fn state_reduce_parent_called_minimally_on_update() {
     #[allow(non_snake_case)]
     #[allow(non_snake_case)]
@@ -446,11 +388,15 @@ struct UnorderedDependanciesState {
 
 
 #[derive(Debug, Clone, Default, PartialEq)]
 #[derive(Debug, Clone, Default, PartialEq)]
 struct ADepCallCounter(usize, BDepCallCounter);
 struct ADepCallCounter(usize, BDepCallCounter);
-impl NodeDepState for ADepCallCounter {
+impl<'a> NodeDepState<(&'a BDepCallCounter,)> for ADepCallCounter {
     type Ctx = ();
     type Ctx = ();
-    type DepState = BDepCallCounter;
     const NODE_MASK: NodeMask = NodeMask::NONE;
     const NODE_MASK: NodeMask = NodeMask::NONE;
-    fn reduce(&mut self, _node: NodeView, sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
+    fn reduce(
+        &mut self,
+        _node: NodeView,
+        (sibling,): (&'a BDepCallCounter,),
+        _ctx: &Self::Ctx,
+    ) -> bool {
         self.0 += 1;
         self.0 += 1;
         self.1 = sibling.clone();
         self.1 = sibling.clone();
         true
         true
@@ -459,11 +405,15 @@ impl NodeDepState for ADepCallCounter {
 
 
 #[derive(Debug, Clone, Default, PartialEq)]
 #[derive(Debug, Clone, Default, PartialEq)]
 struct BDepCallCounter(usize, CDepCallCounter);
 struct BDepCallCounter(usize, CDepCallCounter);
-impl NodeDepState for BDepCallCounter {
+impl<'a> NodeDepState<(&'a CDepCallCounter,)> for BDepCallCounter {
     type Ctx = ();
     type Ctx = ();
-    type DepState = CDepCallCounter;
     const NODE_MASK: NodeMask = NodeMask::NONE;
     const NODE_MASK: NodeMask = NodeMask::NONE;
-    fn reduce(&mut self, _node: NodeView, sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
+    fn reduce(
+        &mut self,
+        _node: NodeView,
+        (sibling,): (&'a CDepCallCounter,),
+        _ctx: &Self::Ctx,
+    ) -> bool {
         self.0 += 1;
         self.0 += 1;
         self.1 = sibling.clone();
         self.1 = sibling.clone();
         true
         true
@@ -472,11 +422,10 @@ impl NodeDepState for BDepCallCounter {
 
 
 #[derive(Debug, Clone, Default, PartialEq)]
 #[derive(Debug, Clone, Default, PartialEq)]
 struct CDepCallCounter(usize);
 struct CDepCallCounter(usize);
-impl NodeDepState for CDepCallCounter {
+impl NodeDepState<()> for CDepCallCounter {
     type Ctx = ();
     type Ctx = ();
-    type DepState = ();
     const NODE_MASK: NodeMask = NodeMask::ALL;
     const NODE_MASK: NodeMask = NodeMask::ALL;
-    fn reduce(&mut self, _node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
+    fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
         self.0 += 1;
         self.0 += 1;
         true
         true
     }
     }

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

@@ -2,4 +2,6 @@ pub mod layout_attributes;
 pub mod node_ref;
 pub mod node_ref;
 pub mod real_dom;
 pub mod real_dom;
 pub mod state;
 pub mod state;
+#[doc(hidden)]
+pub mod traversable;
 pub mod utils;
 pub mod utils;

+ 2 - 2
packages/native-core/src/node_ref.rs

@@ -70,7 +70,7 @@ impl<'a> NodeView<'a> {
     }
     }
 }
 }
 
 
-#[derive(PartialEq, Clone, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
 pub enum AttributeMask {
 pub enum AttributeMask {
     All,
     All,
     Dynamic(Vec<&'static str>),
     Dynamic(Vec<&'static str>),
@@ -175,7 +175,7 @@ impl Default for AttributeMask {
     }
     }
 }
 }
 
 
-#[derive(Default, PartialEq, Clone, Debug)]
+#[derive(Default, PartialEq, Eq, Clone, Debug)]
 pub struct NodeMask {
 pub struct NodeMask {
     // must be sorted
     // must be sorted
     attritutes: AttributeMask,
     attritutes: AttributeMask,

+ 96 - 382
packages/native-core/src/real_dom.rs

@@ -1,17 +1,12 @@
 use anymap::AnyMap;
 use anymap::AnyMap;
 use fxhash::{FxHashMap, FxHashSet};
 use fxhash::{FxHashMap, FxHashSet};
-use std::{
-    collections::VecDeque,
-    ops::{Index, IndexMut},
-};
+use std::ops::{Index, IndexMut};
 
 
 use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
 use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
 
 
-use crate::state::{union_ordered_iter, State};
-use crate::{
-    node_ref::{AttributeMask, NodeMask},
-    state::MemberId,
-};
+use crate::node_ref::{AttributeMask, NodeMask};
+use crate::state::State;
+use crate::traversable::Traversable;
 
 
 /// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
 /// 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.
 /// The render state passes from parent to children and or accumulates state from children to parents.
@@ -20,7 +15,7 @@ use crate::{
 pub struct RealDom<S: State> {
 pub struct RealDom<S: State> {
     root: usize,
     root: usize,
     nodes: Vec<Option<Node<S>>>,
     nodes: Vec<Option<Node<S>>>,
-    nodes_listening: FxHashMap<&'static str, FxHashSet<usize>>,
+    nodes_listening: FxHashMap<&'static str, FxHashSet<ElementId>>,
     node_stack: smallvec::SmallVec<[usize; 10]>,
     node_stack: smallvec::SmallVec<[usize; 10]>,
 }
 }
 
 
@@ -51,7 +46,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.
     /// 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, NodeMask)> {
+    pub fn apply_mutations(&mut self, mutations_vec: Vec<Mutations>) -> Vec<(ElementId, NodeMask)> {
         let mut nodes_updated = Vec::new();
         let mut nodes_updated = Vec::new();
         for mutations in mutations_vec {
         for mutations in mutations_vec {
             for e in mutations.edits {
             for e in mutations.edits {
@@ -72,40 +67,44 @@ impl<S: State> RealDom<S> {
                             .drain(self.node_stack.len() - many as usize..)
                             .drain(self.node_stack.len() - many as usize..)
                             .collect();
                             .collect();
                         for ns in drained {
                         for ns in drained {
-                            self.link_child(ns, target).unwrap();
-                            nodes_updated.push((ns, NodeMask::ALL));
+                            let id = ElementId(ns);
+                            self.link_child(id, ElementId(target)).unwrap();
+                            nodes_updated.push((id, NodeMask::ALL));
                         }
                         }
                     }
                     }
                     ReplaceWith { root, m } => {
                     ReplaceWith { root, m } => {
-                        let root = self.remove(root as usize).unwrap();
+                        let root = self.remove(ElementId(root as usize)).unwrap();
                         let target = root.parent.unwrap().0;
                         let target = root.parent.unwrap().0;
                         let drained: Vec<_> = self.node_stack.drain(0..m as usize).collect();
                         let drained: Vec<_> = self.node_stack.drain(0..m as usize).collect();
                         for ns in drained {
                         for ns in drained {
-                            nodes_updated.push((ns, NodeMask::ALL));
-                            self.link_child(ns, target).unwrap();
+                            let id = ElementId(ns);
+                            nodes_updated.push((id, NodeMask::ALL));
+                            self.link_child(id, ElementId(target)).unwrap();
                         }
                         }
                     }
                     }
                     InsertAfter { root, n } => {
                     InsertAfter { root, n } => {
-                        let target = self[root as usize].parent.unwrap().0;
+                        let target = self[ElementId(root as usize)].parent.unwrap().0;
                         let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
                         let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
                         for ns in drained {
                         for ns in drained {
-                            nodes_updated.push((ns, NodeMask::ALL));
-                            self.link_child(ns, target).unwrap();
+                            let id = ElementId(ns);
+                            nodes_updated.push((id, NodeMask::ALL));
+                            self.link_child(id, ElementId(target)).unwrap();
                         }
                         }
                     }
                     }
                     InsertBefore { root, n } => {
                     InsertBefore { root, n } => {
-                        let target = self[root as usize].parent.unwrap().0;
+                        let target = self[ElementId(root as usize)].parent.unwrap().0;
                         let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
                         let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
                         for ns in drained {
                         for ns in drained {
-                            nodes_updated.push((ns, NodeMask::ALL));
-                            self.link_child(ns, target).unwrap();
+                            let id = ElementId(ns);
+                            nodes_updated.push((id, NodeMask::ALL));
+                            self.link_child(id, ElementId(target)).unwrap();
                         }
                         }
                     }
                     }
                     Remove { root } => {
                     Remove { root } => {
-                        if let Some(parent) = self[root as usize].parent {
-                            nodes_updated.push((parent.0, NodeMask::NONE));
+                        if let Some(parent) = self[ElementId(root as usize)].parent {
+                            nodes_updated.push((parent, NodeMask::NONE));
                         }
                         }
-                        self.remove(root as usize).unwrap();
+                        self.remove(ElementId(root as usize)).unwrap();
                     }
                     }
                     CreateTextNode { root, text } => {
                     CreateTextNode { root, text } => {
                         let n = Node::new(
                         let n = Node::new(
@@ -152,26 +151,29 @@ impl<S: State> RealDom<S> {
                         scope: _,
                         scope: _,
                         root,
                         root,
                     } => {
                     } => {
-                        nodes_updated.push((root as usize, NodeMask::new().with_listeners()));
+                        let id = ElementId(root as usize);
+                        nodes_updated.push((id, NodeMask::new().with_listeners()));
                         if let Some(v) = self.nodes_listening.get_mut(event_name) {
                         if let Some(v) = self.nodes_listening.get_mut(event_name) {
-                            v.insert(root as usize);
+                            v.insert(id);
                         } else {
                         } else {
                             let mut hs = FxHashSet::default();
                             let mut hs = FxHashSet::default();
-                            hs.insert(root as usize);
+                            hs.insert(id);
                             self.nodes_listening.insert(event_name, hs);
                             self.nodes_listening.insert(event_name, hs);
                         }
                         }
                     }
                     }
                     RemoveEventListener { root, event } => {
                     RemoveEventListener { root, event } => {
-                        nodes_updated.push((root as usize, NodeMask::new().with_listeners()));
+                        let id = ElementId(root as usize);
+                        nodes_updated.push((id, NodeMask::new().with_listeners()));
                         let v = self.nodes_listening.get_mut(event).unwrap();
                         let v = self.nodes_listening.get_mut(event).unwrap();
-                        v.remove(&(root as usize));
+                        v.remove(&id);
                     }
                     }
                     SetText {
                     SetText {
                         root,
                         root,
                         text: new_text,
                         text: new_text,
                     } => {
                     } => {
-                        let target = &mut self[root as usize];
-                        nodes_updated.push((root as usize, NodeMask::new().with_text()));
+                        let id = ElementId(root as usize);
+                        let target = &mut self[id];
+                        nodes_updated.push((id, NodeMask::new().with_text()));
                         match &mut target.node_type {
                         match &mut target.node_type {
                             NodeType::Text { text } => {
                             NodeType::Text { text } => {
                                 *text = new_text.to_string();
                                 *text = new_text.to_string();
@@ -180,18 +182,16 @@ impl<S: State> RealDom<S> {
                         }
                         }
                     }
                     }
                     SetAttribute { root, field, .. } => {
                     SetAttribute { root, field, .. } => {
-                        nodes_updated.push((
-                            root as usize,
-                            NodeMask::new_with_attrs(AttributeMask::single(field)),
-                        ));
+                        let id = ElementId(root as usize);
+                        nodes_updated
+                            .push((id, NodeMask::new_with_attrs(AttributeMask::single(field))));
                     }
                     }
                     RemoveAttribute {
                     RemoveAttribute {
                         root, name: field, ..
                         root, name: field, ..
                     } => {
                     } => {
-                        nodes_updated.push((
-                            root as usize,
-                            NodeMask::new_with_attrs(AttributeMask::single(field)),
-                        ));
+                        let id = ElementId(root as usize);
+                        nodes_updated
+                            .push((id, NodeMask::new_with_attrs(AttributeMask::single(field))));
                     }
                     }
                     PopRoot {} => {
                     PopRoot {} => {
                         self.node_stack.pop();
                         self.node_stack.pop();
@@ -203,309 +203,60 @@ impl<S: State> RealDom<S> {
         nodes_updated
         nodes_updated
     }
     }
 
 
-    /// Seperated from apply_mutations because Mutations require a mutable reference to the VirtualDom.
     pub fn update_state(
     pub fn update_state(
         &mut self,
         &mut self,
         vdom: &VirtualDom,
         vdom: &VirtualDom,
-        nodes_updated: Vec<(usize, NodeMask)>,
+        nodes_updated: Vec<(ElementId, NodeMask)>,
         ctx: AnyMap,
         ctx: AnyMap,
-    ) -> Option<FxHashSet<usize>> {
-        #[derive(PartialEq, Clone, Debug)]
-        enum StatesToCheck {
-            All,
-            Some(Vec<MemberId>),
-        }
-        impl StatesToCheck {
-            fn union(&self, other: &Self) -> Self {
-                match (self, other) {
-                    (Self::Some(s), Self::Some(o)) => Self::Some(union_ordered_iter(
-                        s.iter().copied(),
-                        o.iter().copied(),
-                        s.len() + o.len(),
-                    )),
-                    _ => Self::All,
-                }
-            }
-        }
-
-        #[derive(Debug, Clone)]
-        struct NodeRef {
-            id: usize,
-            height: u16,
-            node_mask: NodeMask,
-            to_check: StatesToCheck,
-        }
-        impl NodeRef {
-            fn union_with(&mut self, other: &Self) {
-                self.node_mask = self.node_mask.union(&other.node_mask);
-                self.to_check = self.to_check.union(&other.to_check);
-            }
-        }
-        impl Eq for NodeRef {}
-        impl PartialEq for NodeRef {
-            fn eq(&self, other: &Self) -> bool {
-                self.id == other.id && self.height == other.height
-            }
-        }
-        impl Ord for NodeRef {
-            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-                self.partial_cmp(other).unwrap()
-            }
-        }
-        impl PartialOrd for NodeRef {
-            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-                // Sort nodes first by height, then if the height is the same id.
-                // The order of the id does not matter it just helps with binary search later
-                Some(self.height.cmp(&other.height).then(self.id.cmp(&other.id)))
-            }
-        }
-
-        let mut to_rerender = FxHashSet::default();
-        to_rerender.extend(nodes_updated.iter().map(|(id, _)| id));
-        let mut nodes_updated: Vec<_> = nodes_updated
-            .into_iter()
-            .map(|(id, mask)| NodeRef {
-                id,
-                height: self[id].height,
-                node_mask: mask,
-                to_check: StatesToCheck::All,
-            })
-            .collect();
-        nodes_updated.sort();
-
-        // Combine mutations that affect the same node.
-        let mut last_node: Option<NodeRef> = None;
-        let mut new_nodes_updated = VecDeque::new();
-        for current in nodes_updated.into_iter() {
-            if let Some(node) = &mut last_node {
-                if *node == current {
-                    node.union_with(&current);
-                } else {
-                    new_nodes_updated.push_back(last_node.replace(current).unwrap());
-                }
-            } else {
-                last_node = Some(current);
-            }
-        }
-        if let Some(node) = last_node {
-            new_nodes_updated.push_back(node);
-        }
-        let nodes_updated = new_nodes_updated;
-
-        // update the state that only depends on nodes. The order does not matter.
-        for node_ref in &nodes_updated {
-            let mut changed = false;
-            let node = &mut self[node_ref.id];
-            let mut ids = match &node_ref.to_check {
-                StatesToCheck::All => node.state.node_dep_types(&node_ref.node_mask),
-                // this should only be triggered from the current node, so all members will need to be checked
-                StatesToCheck::Some(_) => unreachable!(),
-            };
-            let mut i = 0;
-            while i < ids.len() {
-                let id = ids[i];
-                let node = &mut self[node_ref.id];
-                let vnode = node.element(vdom);
-                if let Some(members_effected) =
-                    node.state.update_node_dep_state(id, vnode, vdom, &ctx)
-                {
-                    debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
-                    for m in &members_effected.node_dep {
-                        if let Err(idx) = ids.binary_search(m) {
-                            ids.insert(idx, *m);
-                        }
-                    }
-                    changed = true;
-                }
-                i += 1;
-            }
-            if changed {
-                to_rerender.insert(node_ref.id);
-            }
-        }
-
-        // bubble up state. To avoid calling reduce more times than nessisary start from the bottom and go up.
-        let mut to_bubble = nodes_updated.clone();
-        while let Some(node_ref) = to_bubble.pop_back() {
-            let NodeRef {
-                id,
-                height,
-                node_mask,
-                to_check,
-            } = node_ref;
-            let (node, children) = self.get_node_children_mut(id).unwrap();
-            let children_state: Vec<_> = children.iter().map(|c| &c.state).collect();
-            let mut ids = match to_check {
-                StatesToCheck::All => node.state.child_dep_types(&node_mask),
-                StatesToCheck::Some(ids) => ids,
-            };
-            let mut changed = Vec::new();
-            let mut i = 0;
-            while i < ids.len() {
-                let id = ids[i];
-                let vnode = node.element(vdom);
-                if let Some(members_effected) =
-                    node.state
-                        .update_child_dep_state(id, vnode, vdom, &children_state, &ctx)
-                {
-                    debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
-                    for m in members_effected.node_dep {
-                        if let Err(idx) = ids.binary_search(&m) {
-                            ids.insert(idx, m);
-                        }
-                    }
-                    for m in members_effected.child_dep {
-                        changed.push(m);
-                    }
-                }
-                i += 1;
-            }
-            if let Some(parent_id) = node.parent {
-                if !changed.is_empty() {
-                    to_rerender.insert(id);
-                    let i = to_bubble.partition_point(
-                        |NodeRef {
-                             id: other_id,
-                             height: 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].id == parent_id.0 {
-                        to_bubble[i].to_check =
-                            to_bubble[i].to_check.union(&StatesToCheck::Some(changed));
-                    } else {
-                        to_bubble.insert(
-                            i,
-                            NodeRef {
-                                id: parent_id.0,
-                                height: height - 1,
-                                node_mask: NodeMask::NONE,
-                                to_check: StatesToCheck::Some(changed),
-                            },
-                        );
-                    }
-                }
-            }
-        }
-
-        // push down state. To avoid calling reduce more times than nessisary start from the top and go down.
-        let mut to_push = nodes_updated;
-        while let Some(node_ref) = to_push.pop_front() {
-            let NodeRef {
-                id,
-                height,
-                node_mask,
-                to_check,
-            } = node_ref;
-            let node = &self[id];
-            let mut ids = match to_check {
-                StatesToCheck::All => node.state.parent_dep_types(&node_mask),
-                StatesToCheck::Some(ids) => ids,
-            };
-            let mut changed = Vec::new();
-            let (node, parent) = self.get_node_parent_mut(id).unwrap();
-            let mut i = 0;
-            while i < ids.len() {
-                let id = ids[i];
-                let vnode = node.element(vdom);
-                let parent = parent.as_deref();
-                let state = &mut node.state;
-                if let Some(members_effected) =
-                    state.update_parent_dep_state(id, vnode, vdom, parent.map(|n| &n.state), &ctx)
-                {
-                    debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
-                    for m in members_effected.node_dep {
-                        if let Err(idx) = ids.binary_search(&m) {
-                            ids.insert(idx, m);
-                        }
-                    }
-                    for m in members_effected.parent_dep {
-                        changed.push(m);
-                    }
-                }
-                i += 1;
-            }
-
-            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(
-                            |NodeRef {
-                                 id: other_id,
-                                 height: h,
-                                 ..
-                             }| {
-                                *h < height + 1 || (*h == height + 1 && *other_id < c.0)
-                            },
-                        );
-                        if i < to_push.len() && to_push[i].id == c.0 {
-                            to_push[i].to_check = to_push[i]
-                                .to_check
-                                .union(&StatesToCheck::Some(changed.clone()));
-                        } else {
-                            to_push.insert(
-                                i,
-                                NodeRef {
-                                    id: c.0,
-                                    height: height + 1,
-                                    node_mask: NodeMask::NONE,
-                                    to_check: StatesToCheck::Some(changed.clone()),
-                                },
-                            );
-                        }
-                    }
-                }
-            }
-        }
-
-        Some(to_rerender)
+    ) -> FxHashSet<ElementId> {
+        S::update(
+            &nodes_updated,
+            &mut self.map(|n| &n.state, |n| &mut n.state),
+            vdom,
+            &ctx,
+        )
     }
     }
 
 
-    fn link_child(&mut self, child_id: usize, parent_id: usize) -> Option<()> {
+    fn link_child(&mut self, child_id: ElementId, parent_id: ElementId) -> Option<()> {
         debug_assert_ne!(child_id, parent_id);
         debug_assert_ne!(child_id, parent_id);
         let parent = &mut self[parent_id];
         let parent = &mut self[parent_id];
-        parent.add_child(ElementId(child_id));
+        parent.add_child(child_id);
         let parent_height = parent.height + 1;
         let parent_height = parent.height + 1;
-        self[child_id].set_parent(ElementId(parent_id));
+        self[child_id].set_parent(parent_id);
         self.increase_height(child_id, parent_height);
         self.increase_height(child_id, parent_height);
         Some(())
         Some(())
     }
     }
 
 
-    fn increase_height(&mut self, id: usize, amount: u16) {
+    fn increase_height(&mut self, id: ElementId, amount: u16) {
         let n = &mut self[id];
         let n = &mut self[id];
         n.height += amount;
         n.height += amount;
         if let NodeType::Element { children, .. } = &n.node_type {
         if let NodeType::Element { children, .. } = &n.node_type {
             for c in children.clone() {
             for c in children.clone() {
-                self.increase_height(c.0, amount);
+                self.increase_height(c, amount);
             }
             }
         }
         }
     }
     }
 
 
     // remove a node and it's children from the dom.
     // remove a node and it's children from the dom.
-    fn remove(&mut self, id: usize) -> Option<Node<S>> {
+    fn remove(&mut self, id: ElementId) -> Option<Node<S>> {
         // We do not need to remove the node from the parent's children list for children.
         // We do not need to remove the node from the parent's children list for children.
-        fn inner<S: State>(dom: &mut RealDom<S>, id: usize) -> Option<Node<S>> {
-            let mut node = dom.nodes[id as usize].take()?;
+        fn inner<S: State>(dom: &mut RealDom<S>, id: ElementId) -> Option<Node<S>> {
+            let mut node = dom.nodes[id.0].take()?;
             if let NodeType::Element { children, .. } = &mut node.node_type {
             if let NodeType::Element { children, .. } = &mut node.node_type {
                 for c in children {
                 for c in children {
-                    inner(dom, c.0)?;
+                    inner(dom, *c)?;
                 }
                 }
             }
             }
             Some(node)
             Some(node)
         }
         }
-        let mut node = self.nodes[id as usize].take()?;
+        let mut node = self.nodes[id.0].take()?;
         if let Some(parent) = node.parent {
         if let Some(parent) = node.parent {
             let parent = &mut self[parent];
             let parent = &mut self[parent];
-            parent.remove_child(ElementId(id));
+            parent.remove_child(id);
         }
         }
         if let NodeType::Element { children, .. } = &mut node.node_type {
         if let NodeType::Element { children, .. } = &mut node.node_type {
             for c in children {
             for c in children {
-                inner(self, c.0)?;
+                inner(self, *c)?;
             }
             }
         }
         }
         Some(node)
         Some(node)
@@ -521,62 +272,6 @@ impl<S: State> RealDom<S> {
         self.nodes[id] = Some(node);
         self.nodes[id] = Some(node);
     }
     }
 
 
-    pub fn get(&self, id: usize) -> Option<&Node<S>> {
-        self.nodes.get(id)?.as_ref()
-    }
-
-    pub fn get_mut(&mut self, id: usize) -> Option<&mut Node<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(
-        &mut self,
-        id: usize,
-    ) -> Option<(&mut Node<S>, Vec<&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(),
-                    };
-                    Some((node, children))
-                } else {
-                    None
-                }
-            }
-        }
-    }
-
-    // this is safe because no node will have itself as a parent
-    pub fn get_node_parent_mut(
-        &mut self,
-        id: usize,
-    ) -> Option<(&mut Node<S>, Option<&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());
-                Some((node, parent))
-            } else {
-                None
-            }
-        }
-    }
-
     pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S>> {
     pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S>> {
         if let Some(nodes) = self.nodes_listening.get(event) {
         if let Some(nodes) = self.nodes_listening.get(event) {
             let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
             let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
@@ -658,7 +353,7 @@ impl<S: State> RealDom<S> {
                 }
                 }
             }
             }
         }
         }
-        if let NodeType::Element { children, .. } = &self[self.root].node_type {
+        if let NodeType::Element { children, .. } = &self[ElementId(self.root)].node_type {
             for c in children {
             for c in children {
                 inner(self, *c, &mut f);
                 inner(self, *c, &mut f);
             }
             }
@@ -677,7 +372,7 @@ impl<S: State> RealDom<S> {
             }
             }
         }
         }
         let root = self.root;
         let root = self.root;
-        if let NodeType::Element { children, .. } = &mut self[root].node_type {
+        if let NodeType::Element { children, .. } = &mut self[ElementId(root)].node_type {
             for c in children.clone() {
             for c in children.clone() {
                 inner(self, c, &mut f);
                 inner(self, c, &mut f);
             }
             }
@@ -685,30 +380,17 @@ impl<S: State> RealDom<S> {
     }
     }
 }
 }
 
 
-impl<S: State> Index<usize> for RealDom<S> {
-    type Output = Node<S>;
-
-    fn index(&self, idx: usize) -> &Self::Output {
-        self.get(idx).expect("Node does not exist")
-    }
-}
-
 impl<S: State> Index<ElementId> for RealDom<S> {
 impl<S: State> Index<ElementId> for RealDom<S> {
     type Output = Node<S>;
     type Output = Node<S>;
 
 
     fn index(&self, idx: ElementId) -> &Self::Output {
     fn index(&self, idx: ElementId) -> &Self::Output {
-        &self[idx.0]
+        self.get(idx).unwrap()
     }
     }
 }
 }
 
 
-impl<S: State> IndexMut<usize> for RealDom<S> {
-    fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
-        self.get_mut(idx).expect("Node does not exist")
-    }
-}
 impl<S: State> IndexMut<ElementId> for RealDom<S> {
 impl<S: State> IndexMut<ElementId> for RealDom<S> {
     fn index_mut(&mut self, idx: ElementId) -> &mut Self::Output {
     fn index_mut(&mut self, idx: ElementId) -> &mut Self::Output {
-        &mut self[idx.0]
+        self.get_mut(idx).unwrap()
     }
     }
 }
 }
 
 
@@ -772,3 +454,35 @@ impl<S: State> Node<S> {
         self.parent = Some(parent);
         self.parent = Some(parent);
     }
     }
 }
 }
+
+impl<T: State> Traversable for RealDom<T> {
+    type Id = ElementId;
+    type Node = Node<T>;
+
+    fn height(&self, id: Self::Id) -> Option<u16> {
+        Some(<Self as Traversable>::get(self, id)?.height)
+    }
+
+    fn get(&self, id: Self::Id) -> Option<&Self::Node> {
+        self.nodes.get(id.0)?.as_ref()
+    }
+
+    fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> {
+        self.nodes.get_mut(id.0)?.as_mut()
+    }
+
+    fn children(&self, id: Self::Id) -> &[Self::Id] {
+        if let Some(node) = <Self as Traversable>::get(self, id) {
+            match &node.node_type {
+                NodeType::Element { children, .. } => children,
+                _ => &[],
+            }
+        } else {
+            &[]
+        }
+    }
+
+    fn parent(&self, id: Self::Id) -> Option<Self::Id> {
+        <Self as Traversable>::get(self, id).and_then(|n| n.parent)
+    }
+}

+ 67 - 92
packages/native-core/src/state.rs

@@ -1,13 +1,11 @@
-use std::{
-    cmp::Ordering,
-    fmt::Debug,
-    ops::{Add, AddAssign, Sub, SubAssign},
-};
+use std::{cmp::Ordering, fmt::Debug};
 
 
 use anymap::AnyMap;
 use anymap::AnyMap;
-use dioxus_core::VNode;
+use dioxus_core::ElementId;
+use fxhash::FxHashSet;
 
 
 use crate::node_ref::{NodeMask, NodeView};
 use crate::node_ref::{NodeMask, NodeView};
+use crate::traversable::Traversable;
 
 
 pub(crate) fn union_ordered_iter<T: Ord + Debug>(
 pub(crate) fn union_ordered_iter<T: Ord + Debug>(
     s_iter: impl Iterator<Item = T>,
     s_iter: impl Iterator<Item = T>,
@@ -50,7 +48,7 @@ pub trait ChildDepState {
     /// The context is passed to the [ChildDepState::reduce] when it is pushed down.
     /// The context is passed to the [ChildDepState::reduce] when it is pushed down.
     /// This is sometimes nessisary for lifetime purposes.
     /// This is sometimes nessisary for lifetime purposes.
     type Ctx;
     type Ctx;
-    /// This must be either a [ChildDepState] or [NodeDepState]
+    /// This must be either a [ChildDepState], or [NodeDepState]
     type DepState;
     type DepState;
     const NODE_MASK: NodeMask = NodeMask::NONE;
     const NODE_MASK: NodeMask = NodeMask::NONE;
     fn reduce<'a>(
     fn reduce<'a>(
@@ -73,70 +71,77 @@ pub trait ParentDepState {
     /// This must be either a [ParentDepState] or [NodeDepState]
     /// This must be either a [ParentDepState] or [NodeDepState]
     type DepState;
     type DepState;
     const NODE_MASK: NodeMask = NodeMask::NONE;
     const NODE_MASK: NodeMask = NodeMask::NONE;
-    fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool;
+    fn reduce<'a>(
+        &mut self,
+        node: NodeView,
+        parent: Option<&'a 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.
 /// 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 sibling's [NodeDepState] is modified.
 /// Called when the current node's node properties are modified or a sibling's [NodeDepState] is modified.
 /// Called at most once per update.
 /// Called at most once per update.
-pub trait NodeDepState {
+/// NodeDepState is the only state that can accept multiple dependancies, but only from the current node.
+/// ```rust
+/// impl<'a, 'b> NodeDepState<(&'a TextWrap, &'b ChildLayout)> for Layout {
+///     type Ctx = LayoutCache;
+///     const NODE_MASK: NodeMask =
+///         NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!([
+///             "width", "height"
+///         ])))
+///         .with_text();
+///     fn reduce<'a>(
+///         &mut self,
+///         node: NodeView,
+///         siblings: (&'a TextWrap, &'b ChildLayout),
+///         ctx: &Self::Ctx,
+///     ) -> bool {
+///         let old = self.clone();
+///         let (text_wrap, child_layout) = siblings;
+///         if TextWrap::Wrap == text_wrap {
+///             if let Some(text) = node.text() {
+///                 let lines = text_wrap.get_lines(text);
+///                 self.width = lines.max_by(|l| l.len());
+///                 self.height = lines.len();
+///                 return old != self;
+///             }
+///         }
+///         let mut width = child_layout.width;
+///         let mut height = child_layout.width;
+///         for attr in node.attributes() {
+///             match attr.name {
+///                 "width" => {
+///                     width = attr.value.as_text().unwrap().parse().unwrap();
+///                 }
+///                 "height" => {
+///                     height = attr.value.as_text().unwrap().parse().unwrap();
+///                 }
+///                 _ => unreachable!(),
+///             }
+///         }
+///         self.width = width;
+///         self.height = height;
+///         old != self
+///     }
+/// }
+/// ```
+/// The generic argument (Depstate) must be a tuple containing any number of borrowed elments that are either a [ChildDepState], [ParentDepState] or [NodeDepState].
+pub trait NodeDepState<DepState> {
     type Ctx;
     type Ctx;
-    type DepState: NodeDepState;
     const NODE_MASK: NodeMask = NodeMask::NONE;
     const NODE_MASK: NodeMask = NodeMask::NONE;
-    fn reduce(&mut self, node: NodeView, sibling: &Self::DepState, ctx: &Self::Ctx) -> bool;
-}
-
-#[derive(Debug)]
-pub struct ChildStatesChanged {
-    pub node_dep: Vec<MemberId>,
-    pub child_dep: Vec<MemberId>,
-}
-
-#[derive(Debug)]
-pub struct ParentStatesChanged {
-    pub node_dep: Vec<MemberId>,
-    pub parent_dep: Vec<MemberId>,
-}
-
-#[derive(Debug)]
-pub struct NodeStatesChanged {
-    pub node_dep: Vec<MemberId>,
+    fn reduce(&mut self, node: NodeView, siblings: DepState, ctx: &Self::Ctx) -> bool;
 }
 }
 
 
+/// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
 pub trait State: Default + Clone {
 pub trait State: Default + Clone {
-    const SIZE: usize;
-
-    fn update_node_dep_state<'a>(
-        &'a mut self,
-        ty: MemberId,
-        node: &'a VNode<'a>,
-        vdom: &'a dioxus_core::VirtualDom,
-        ctx: &AnyMap,
-    ) -> Option<NodeStatesChanged>;
-    /// This must be a valid resolution order. (no nodes updated before a state they rely on)
-    fn child_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
-
-    fn update_parent_dep_state<'a>(
-        &'a mut self,
-        ty: MemberId,
-        node: &'a VNode<'a>,
+    #[doc(hidden)]
+    fn update<'a, T: Traversable<Node = Self, Id = ElementId>>(
+        dirty: &[(ElementId, NodeMask)],
+        state_tree: &'a mut T,
         vdom: &'a dioxus_core::VirtualDom,
         vdom: &'a dioxus_core::VirtualDom,
-        parent: Option<&Self>,
         ctx: &AnyMap,
         ctx: &AnyMap,
-    ) -> Option<ParentStatesChanged>;
-    /// This must be a valid resolution order. (no nodes updated before a state they rely on)
-    fn parent_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
-
-    fn update_child_dep_state<'a>(
-        &'a mut self,
-        ty: MemberId,
-        node: &'a VNode<'a>,
-        vdom: &'a dioxus_core::VirtualDom,
-        children: &[&Self],
-        ctx: &AnyMap,
-    ) -> Option<ChildStatesChanged>;
-    /// This must be a valid resolution order. (no nodes updated before a state they rely on)
-    fn node_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
+    ) -> FxHashSet<ElementId>;
 }
 }
 
 
 // Todo: once GATs land we can model multable dependencies
 // Todo: once GATs land we can model multable dependencies
@@ -159,44 +164,14 @@ impl ChildDepState for () {
 impl ParentDepState for () {
 impl ParentDepState for () {
     type Ctx = ();
     type Ctx = ();
     type DepState = ();
     type DepState = ();
-    fn reduce(&mut self, _: NodeView, _: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
+    fn reduce<'a>(&mut self, _: NodeView, _: Option<&'a Self::DepState>, _: &Self::Ctx) -> bool {
         false
         false
     }
     }
 }
 }
 
 
-impl NodeDepState for () {
+impl NodeDepState<()> for () {
     type Ctx = ();
     type Ctx = ();
-    type DepState = ();
-    fn reduce(&mut self, _: NodeView, _sibling: &Self::DepState, _: &Self::Ctx) -> bool {
+    fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool {
         false
         false
     }
     }
 }
 }
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub struct MemberId(pub usize);
-
-impl Sub<usize> for MemberId {
-    type Output = MemberId;
-    fn sub(self, rhs: usize) -> Self::Output {
-        MemberId(self.0 - rhs)
-    }
-}
-
-impl Add<usize> for MemberId {
-    type Output = MemberId;
-    fn add(self, rhs: usize) -> Self::Output {
-        MemberId(self.0 + rhs)
-    }
-}
-
-impl SubAssign<usize> for MemberId {
-    fn sub_assign(&mut self, rhs: usize) {
-        *self = *self - rhs;
-    }
-}
-
-impl AddAssign<usize> for MemberId {
-    fn add_assign(&mut self, rhs: usize) {
-        *self = *self + rhs;
-    }
-}

+ 100 - 0
packages/native-core/src/traversable.rs

@@ -0,0 +1,100 @@
+pub trait Traversable {
+    type Id: Copy;
+    type Node;
+
+    fn height(&self, id: Self::Id) -> Option<u16>;
+
+    fn get(&self, id: Self::Id) -> Option<&Self::Node>;
+    fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node>;
+
+    fn children(&self, node: Self::Id) -> &[Self::Id];
+    fn parent(&self, node: Self::Id) -> Option<Self::Id>;
+
+    fn map<N, F: Fn(&Self::Node) -> &N, FMut: Fn(&mut Self::Node) -> &mut N>(
+        &mut self,
+        f: F,
+        f_mut: FMut,
+    ) -> Map<Self, N, F, FMut>
+    where
+        Self: Sized,
+    {
+        Map {
+            tree: self,
+            f,
+            f_mut,
+        }
+    }
+
+    // this is safe because no node will have itself as it's parent
+    fn get_node_parent_mut(
+        &mut self,
+        id: Self::Id,
+    ) -> (Option<&mut Self::Node>, Option<&mut Self::Node>) {
+        let node = self.get_mut(id).map(|n| n as *mut _);
+        let parent = self
+            .parent(id)
+            .and_then(|n| self.get_mut(n))
+            .map(|n| n as *mut _);
+        unsafe { (node.map(|n| &mut *n), parent.map(|n| &mut *n)) }
+    }
+
+    // this is safe because no node will have itself as a child
+    fn get_node_children_mut(
+        &mut self,
+        id: Self::Id,
+    ) -> (Option<&mut Self::Node>, Vec<&mut Self::Node>) {
+        let node = self.get_mut(id).map(|n| n as *mut _);
+        let mut children = Vec::new();
+        let children_indexes = self.children(id).to_vec();
+        for id in children_indexes {
+            if let Some(n) = self.get_mut(id) {
+                children.push(unsafe { &mut *(n as *mut _) });
+            }
+        }
+        unsafe { (node.map(|n| &mut *n), children) }
+    }
+}
+
+pub struct Map<
+    'a,
+    T: Traversable,
+    N,
+    F: Fn(&<T as Traversable>::Node) -> &N,
+    FMut: Fn(&mut <T as Traversable>::Node) -> &mut N,
+> {
+    f: F,
+    f_mut: FMut,
+    tree: &'a mut T,
+}
+
+impl<
+        'a,
+        T: Traversable,
+        N,
+        F: Fn(&<T as Traversable>::Node) -> &N,
+        FMut: Fn(&mut <T as Traversable>::Node) -> &mut N,
+    > Traversable for Map<'a, T, N, F, FMut>
+{
+    type Id = <T as Traversable>::Id;
+    type Node = N;
+
+    fn height(&self, id: Self::Id) -> Option<u16> {
+        self.tree.height(id)
+    }
+
+    fn get(&self, id: Self::Id) -> Option<&Self::Node> {
+        self.tree.get(id).map(&self.f)
+    }
+
+    fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> {
+        self.tree.get_mut(id).map(&self.f_mut)
+    }
+
+    fn children(&self, id: Self::Id) -> &[Self::Id] {
+        self.tree.children(id)
+    }
+
+    fn parent(&self, id: Self::Id) -> Option<Self::Id> {
+        self.tree.parent(id)
+    }
+}

+ 2 - 3
packages/tui/src/focus.rs

@@ -62,13 +62,12 @@ pub(crate) struct Focus {
     pub level: FocusLevel,
     pub level: FocusLevel,
 }
 }
 
 
-impl NodeDepState for Focus {
+impl NodeDepState<()> for Focus {
     type Ctx = ();
     type Ctx = ();
-    type DepState = ();
     const NODE_MASK: NodeMask =
     const NODE_MASK: NodeMask =
         NodeMask::new_with_attrs(AttributeMask::Static(FOCUS_ATTRIBUTES)).with_listeners();
         NodeMask::new_with_attrs(AttributeMask::Static(FOCUS_ATTRIBUTES)).with_listeners();
 
 
-    fn reduce(&mut self, node: NodeView<'_>, _sibling: &Self::DepState, _: &Self::Ctx) -> bool {
+    fn reduce(&mut self, node: NodeView<'_>, _sibling: (), _: &Self::Ctx) -> bool {
         let new = Focus {
         let new = Focus {
             level: if let Some(a) = node.attributes().find(|a| a.name == "tabindex") {
             level: if let Some(a) = node.attributes().find(|a| a.name == "tabindex") {
                 if let Some(index) = a
                 if let Some(index) = a

+ 1 - 1
packages/tui/src/hooks.rs

@@ -253,7 +253,7 @@ impl InnerInputState {
                 let mut parent = node.parent;
                 let mut parent = node.parent;
                 while let Some(parent_id) = parent {
                 while let Some(parent_id) = parent {
                     will_bubble.insert(parent_id);
                     will_bubble.insert(parent_id);
-                    parent = dom[parent_id.0].parent;
+                    parent = dom[parent_id].parent;
                 }
                 }
                 resolved_events.push(UserEvent {
                 resolved_events.push(UserEvent {
                     scope_id: None,
                     scope_id: None,

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

@@ -94,7 +94,7 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
         let to_update = rdom.apply_mutations(vec![mutations]);
         let to_update = rdom.apply_mutations(vec![mutations]);
         let mut any_map = AnyMap::new();
         let mut any_map = AnyMap::new();
         any_map.insert(taffy.clone());
         any_map.insert(taffy.clone());
-        let _to_rerender = rdom.update_state(&dom, to_update, any_map).unwrap();
+        let _to_rerender = rdom.update_state(&dom, to_update, any_map);
     }
     }
 
 
     render_vdom(
     render_vdom(
@@ -133,7 +133,8 @@ fn render_vdom(
                 terminal.clear().unwrap();
                 terminal.clear().unwrap();
             }
             }
 
 
-            let mut to_rerender: fxhash::FxHashSet<usize> = vec![0].into_iter().collect();
+            let mut to_rerender: fxhash::FxHashSet<ElementId> =
+                vec![ElementId(0)].into_iter().collect();
             let mut updated = true;
             let mut updated = true;
 
 
             loop {
             loop {
@@ -153,7 +154,7 @@ fn render_vdom(
                     fn resize(dims: Rect, taffy: &mut Taffy, rdom: &Dom) {
                     fn resize(dims: Rect, taffy: &mut Taffy, rdom: &Dom) {
                         let width = dims.width;
                         let width = dims.width;
                         let height = dims.height;
                         let height = dims.height;
-                        let root_node = rdom[0].state.layout.node.unwrap();
+                        let root_node = rdom[ElementId(0)].state.layout.node.unwrap();
 
 
                         taffy
                         taffy
                             .compute_layout(
                             .compute_layout(
@@ -170,7 +171,7 @@ fn render_vdom(
                             let rdom = rdom.borrow();
                             let rdom = rdom.borrow();
                             // size is guaranteed to not change when rendering
                             // size is guaranteed to not change when rendering
                             resize(frame.size(), &mut taffy.borrow_mut(), &rdom);
                             resize(frame.size(), &mut taffy.borrow_mut(), &rdom);
-                            let root = &rdom[0];
+                            let root = &rdom[ElementId(0)];
                             render::render_vnode(
                             render::render_vnode(
                                 frame,
                                 frame,
                                 &taffy.borrow(),
                                 &taffy.borrow(),
@@ -249,7 +250,7 @@ fn render_vdom(
                     // update the style and layout
                     // update the style and layout
                     let mut any_map = AnyMap::new();
                     let mut any_map = AnyMap::new();
                     any_map.insert(taffy.clone());
                     any_map.insert(taffy.clone());
-                    to_rerender = rdom.update_state(vdom, to_update, any_map).unwrap();
+                    to_rerender = rdom.update_state(vdom, to_update, any_map);
                 }
                 }
             }
             }
 
 

+ 2 - 4
packages/tui/src/node.rs

@@ -45,11 +45,9 @@ impl Default for PreventDefault {
     }
     }
 }
 }
 
 
-impl NodeDepState for PreventDefault {
+impl NodeDepState<()> for PreventDefault {
     type Ctx = ();
     type Ctx = ();
 
 
-    type DepState = ();
-
     const NODE_MASK: dioxus_native_core::node_ref::NodeMask =
     const NODE_MASK: dioxus_native_core::node_ref::NodeMask =
         dioxus_native_core::node_ref::NodeMask::new_with_attrs(
         dioxus_native_core::node_ref::NodeMask::new_with_attrs(
             dioxus_native_core::node_ref::AttributeMask::Static(&sorted_str_slice!([
             dioxus_native_core::node_ref::AttributeMask::Static(&sorted_str_slice!([
@@ -60,7 +58,7 @@ impl NodeDepState for PreventDefault {
     fn reduce(
     fn reduce(
         &mut self,
         &mut self,
         node: dioxus_native_core::node_ref::NodeView,
         node: dioxus_native_core::node_ref::NodeView,
-        _sibling: &Self::DepState,
+        _sibling: (),
         _ctx: &Self::Ctx,
         _ctx: &Self::Ctx,
     ) -> bool {
     ) -> bool {
         let new = match node
         let new = match node

+ 1 - 1
packages/tui/src/query.rs

@@ -15,7 +15,7 @@ use crate::Dom;
 /// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered.
 /// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered.
 /// Provided as a root context for all tui applictions.
 /// Provided as a root context for all tui applictions.
 /// # Example
 /// # Example
-/// ```rust
+/// ```rust, ignore
 /// use dioxus::prelude::*;
 /// use dioxus::prelude::*;
 /// use dioxus::tui::query::Query;
 /// use dioxus::tui::query::Query;
 /// use dioxus::tui::Size;
 /// use dioxus::tui::Size;

+ 1 - 1
packages/tui/src/render.rs

@@ -78,7 +78,7 @@ pub(crate) fn render_vnode(
             }
             }
 
 
             for c in children {
             for c in children {
-                render_vnode(frame, layout, rdom, &rdom[c.0], cfg, location);
+                render_vnode(frame, layout, rdom, &rdom[*c], cfg, location);
             }
             }
         }
         }
         NodeType::Placeholder => unreachable!(),
         NodeType::Placeholder => unreachable!(),