Browse Source

Merge branch 'master' of github.com:DioxusLabs/dioxus

Jonathan Kelley 3 years ago
parent
commit
2b9888627b

+ 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
 
-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
 
-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
 
@@ -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.
 
-```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
 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> {
-        if let syn::Expr::Path(path) = &*expr {
+        if let syn::Expr::Path(path) = expr {
             path_to_single_string(&path.path)
         } else {
             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
             // nesting is different so we have to do this little dance.
             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(
                         &field_type,
                         "can't `strip_option` - field is not `Option<...>`",
                     )
-                })?;
-                internal_type
+                })?
             } else {
                 field_type
             };

+ 1 - 1
packages/desktop/Cargo.toml

@@ -21,7 +21,7 @@ serde = "1.0.136"
 serde_json = "1.0.79"
 thiserror = "1.0.30"
 log = "0.4.14"
-wry = { version = "0.16.0" }
+wry = { version = "0.19.0" }
 futures-channel = "0.3.21"
 tokio = { version = "1.16.1", features = [
     "sync",

+ 3 - 2
packages/desktop/src/events.rs

@@ -6,8 +6,9 @@ use std::sync::Arc;
 use dioxus_core::{ElementId, EventPriority, UserEvent};
 use dioxus_html::event_bubbles;
 use dioxus_html::on::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(serde::Serialize, serde::Deserialize)]
+#[derive(Deserialize, Serialize)]
 pub(crate) struct IpcMessage {
     method: String,
     params: serde_json::Value,
@@ -33,7 +34,7 @@ pub(crate) fn parse_ipc_message(payload: &str) -> Option<IpcMessage> {
     }
 }
 
-#[derive(serde::Serialize, serde::Deserialize)]
+#[derive(Deserialize, Serialize)]
 struct ImEvent {
     event: String,
     mounted_dom_id: u64,

+ 0 - 7
packages/desktop/src/lib.rs

@@ -213,13 +213,6 @@ pub fn launch_with_props<P: 'static + Send>(
             } => match event {
                 WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
                 WindowEvent::Destroyed { .. } => desktop.close_window(window_id, control_flow),
-
-                WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
-                    if let Some(view) = desktop.webviews.get_mut(&window_id) {
-                        let _ = view.resize();
-                    }
-                }
-
                 _ => {}
             },
 

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

@@ -937,7 +937,7 @@ pub mod on {
     feature = "serialize",
     derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
 )]
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 #[repr(u8)]
 pub enum KeyCode {
     // 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;
 
-use dioxus_native_core::state::MemberId;
 use proc_macro::TokenStream;
-use quote::format_ident;
-use quote::{quote, ToTokens, __private::Span};
+use quote::{quote, ToTokens};
 use sorted_slice::StrSlice;
+use syn::parenthesized;
+use syn::parse::ParseBuffer;
+use syn::punctuated::Punctuated;
 use syn::{
     self,
     parse::{Parse, ParseStream, Result},
@@ -51,228 +52,99 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
     let strct = Struct::new(type_name.clone(), &fields);
     match StateStruct::parse(&fields, &strct) {
         Ok(state_strct) => {
-            let node_dep_state_fields = state_strct
+            let members: Vec<_> = state_strct
                 .state_members
                 .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
                 .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! {
-                #(
-                    #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,
                         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 {
     name: Ident,
     members: Vec<Member>,
@@ -302,7 +180,7 @@ struct StateStruct<'a> {
 impl<'a> StateStruct<'a> {
     fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result<Self> {
         let mut parse_err = Ok(());
-        let state_members = strct
+        let mut unordered_state_members: Vec<_> = strct
             .members
             .iter()
             .zip(fields.iter())
@@ -312,7 +190,44 @@ impl<'a> StateStruct<'a> {
                     parse_err = Err(err);
                     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
             .members
@@ -328,177 +243,234 @@ impl<'a> StateStruct<'a> {
             })
             .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 {
-                    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 {
     ctx_ty: Option<Type>,
-    dep: Option<Ident>,
+    deps: Vec<Ident>,
 }
 
 impl Parse for Dependancy {
     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 ctx_ty = input.parse().ok();
         Ok(Self {
             ctx_ty: comma.and(ctx_ty),
-            dep,
+            deps,
         })
     }
 }
@@ -522,11 +494,9 @@ impl Member {
 struct StateMember<'a> {
     mem: &'a Member,
     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>,
-    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> {
@@ -548,26 +518,26 @@ impl<'a> StateMember<'a> {
                 })?;
             match a.parse_args::<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 {
                         mem,
                         dep_kind,
-                        dep_mem,
+                        dep_mems,
                         ctx_ty: dependancy.ctx_ty,
-                        dependants: Vec::new(),
-                        member_id: dioxus_native_core::state::MemberId(0),
                     })
                 }
                 Err(e) => {
@@ -592,111 +562,26 @@ impl<'a> StateMember<'a> {
         } else {
             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 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!(&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 mutations = vdom.diff_lazynodes(
@@ -80,7 +80,7 @@ fn remove_node() {
 
     assert_eq!(dom.size(), 1);
     assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
-    assert_eq!(dom[1].height, 1);
+    assert_eq!(dom[ElementId(1)].height, 1);
 }
 
 #[test]
@@ -113,7 +113,7 @@ fn add_node() {
 
     assert_eq!(dom.size(), 1);
     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 mutations = vdom.diff_lazynodes(
@@ -152,6 +152,6 @@ fn add_node() {
 
     assert_eq!(dom.size(), 2);
     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!(&dom.contains_node(&VNode::Element(&root_div)));
-    assert_eq!(dom[1].height, 1);
+    assert_eq!(dom[ElementId(1)].height, 1);
 }
 
 #[test]
@@ -119,10 +119,10 @@ fn initial_build_with_children() {
     };
     assert_eq!(dom.size(), 6);
     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 dioxus::core as dioxus_core;
+use dioxus::core::ElementId;
 use dioxus::core::{AttributeValue, DomEdit, Mutations};
 use dioxus::prelude::*;
 use dioxus_native_core::node_ref::*;
@@ -49,13 +50,14 @@ impl ChildDepState for ChildDepCallCounter {
     const NODE_MASK: NodeMask = NodeMask::ALL;
     fn reduce<'a>(
         &mut self,
-        _: NodeView,
+        node: NodeView,
         _: impl Iterator<Item = &'a Self::DepState>,
         _: &Self::Ctx,
     ) -> bool
     where
         Self::DepState: 'a,
     {
+        println!("{self:?} {:?}: {} {:?}", node.tag(), node.id(), node.text());
         self.0 += 1;
         true
     }
@@ -80,11 +82,10 @@ impl ParentDepState for ParentDepCallCounter {
 
 #[derive(Debug, Clone, Default)]
 struct NodeDepCallCounter(u32);
-impl NodeDepState for NodeDepCallCounter {
+impl NodeDepState<()> for NodeDepCallCounter {
     type Ctx = ();
-    type DepState = ();
     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;
         true
     }
@@ -133,11 +134,10 @@ impl ParentDepState for PushedDownStateTester {
 
 #[derive(Debug, Clone, PartialEq, Default)]
 struct NodeStateTester(Option<String>, Vec<(String, String)>);
-impl NodeDepState for NodeStateTester {
+impl NodeDepState<()> for NodeStateTester {
     type Ctx = u32;
-    type DepState = ();
     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);
         *self = NodeStateTester(
             node.tag().map(|s| s.to_string()),
@@ -187,7 +187,7 @@ fn state_initial() {
     ctx.insert(42u32);
     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.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.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.1, Vec::new());
     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())]
     );
 
-    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.1, Vec::new());
     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![]);
 }
 
-#[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]
 fn state_reduce_parent_called_minimally_on_update() {
     #[allow(non_snake_case)]
@@ -446,11 +388,15 @@ struct UnorderedDependanciesState {
 
 #[derive(Debug, Clone, Default, PartialEq)]
 struct ADepCallCounter(usize, BDepCallCounter);
-impl NodeDepState for ADepCallCounter {
+impl<'a> NodeDepState<(&'a BDepCallCounter,)> for ADepCallCounter {
     type Ctx = ();
-    type DepState = BDepCallCounter;
     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.1 = sibling.clone();
         true
@@ -459,11 +405,15 @@ impl NodeDepState for ADepCallCounter {
 
 #[derive(Debug, Clone, Default, PartialEq)]
 struct BDepCallCounter(usize, CDepCallCounter);
-impl NodeDepState for BDepCallCounter {
+impl<'a> NodeDepState<(&'a CDepCallCounter,)> for BDepCallCounter {
     type Ctx = ();
-    type DepState = CDepCallCounter;
     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.1 = sibling.clone();
         true
@@ -472,11 +422,10 @@ impl NodeDepState for BDepCallCounter {
 
 #[derive(Debug, Clone, Default, PartialEq)]
 struct CDepCallCounter(usize);
-impl NodeDepState for CDepCallCounter {
+impl NodeDepState<()> for CDepCallCounter {
     type Ctx = ();
-    type DepState = ();
     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;
         true
     }

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

@@ -2,4 +2,6 @@ pub mod layout_attributes;
 pub mod node_ref;
 pub mod real_dom;
 pub mod state;
+#[doc(hidden)]
+pub mod traversable;
 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 {
     All,
     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 {
     // must be sorted
     attritutes: AttributeMask,

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

@@ -1,17 +1,12 @@
 use anymap::AnyMap;
 use fxhash::{FxHashMap, FxHashSet};
-use std::{
-    collections::VecDeque,
-    ops::{Index, IndexMut},
-};
+use std::ops::{Index, IndexMut};
 
 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.
 /// 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> {
     root: usize,
     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]>,
 }
 
@@ -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.
-    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();
         for mutations in mutations_vec {
             for e in mutations.edits {
@@ -72,40 +67,44 @@ impl<S: State> RealDom<S> {
                             .drain(self.node_stack.len() - many as usize..)
                             .collect();
                         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 } => {
-                        let root = self.remove(root as usize).unwrap();
+                        let root = self.remove(ElementId(root as usize)).unwrap();
                         let target = root.parent.unwrap().0;
                         let drained: Vec<_> = self.node_stack.drain(0..m as usize).collect();
                         for ns in drained {
-                            nodes_updated.push((ns, 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 } => {
-                        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();
                         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 } => {
-                        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();
                         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 } => {
-                        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 } => {
                         let n = Node::new(
@@ -152,26 +151,29 @@ impl<S: State> RealDom<S> {
                         scope: _,
                         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) {
-                            v.insert(root as usize);
+                            v.insert(id);
                         } else {
                             let mut hs = FxHashSet::default();
-                            hs.insert(root as usize);
+                            hs.insert(id);
                             self.nodes_listening.insert(event_name, hs);
                         }
                     }
                     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();
-                        v.remove(&(root as usize));
+                        v.remove(&id);
                     }
                     SetText {
                         root,
                         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 {
                             NodeType::Text { text } => {
                                 *text = new_text.to_string();
@@ -180,18 +182,16 @@ impl<S: State> RealDom<S> {
                         }
                     }
                     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 {
                         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 {} => {
                         self.node_stack.pop();
@@ -203,309 +203,60 @@ impl<S: State> RealDom<S> {
         nodes_updated
     }
 
-    /// Seperated from apply_mutations because Mutations require a mutable reference to the VirtualDom.
     pub fn update_state(
         &mut self,
         vdom: &VirtualDom,
-        nodes_updated: Vec<(usize, NodeMask)>,
+        nodes_updated: Vec<(ElementId, NodeMask)>,
         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);
         let parent = &mut self[parent_id];
-        parent.add_child(ElementId(child_id));
+        parent.add_child(child_id);
         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);
         Some(())
     }
 
-    fn increase_height(&mut self, id: usize, amount: u16) {
+    fn increase_height(&mut self, id: ElementId, amount: u16) {
         let n = &mut self[id];
         n.height += amount;
         if let NodeType::Element { children, .. } = &n.node_type {
             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.
-    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.
-        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 {
                 for c in children {
-                    inner(dom, c.0)?;
+                    inner(dom, *c)?;
                 }
             }
             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 {
             let parent = &mut self[parent];
-            parent.remove_child(ElementId(id));
+            parent.remove_child(id);
         }
         if let NodeType::Element { children, .. } = &mut node.node_type {
             for c in children {
-                inner(self, c.0)?;
+                inner(self, *c)?;
             }
         }
         Some(node)
@@ -521,62 +272,6 @@ impl<S: State> RealDom<S> {
         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>> {
         if let Some(nodes) = self.nodes_listening.get(event) {
             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 {
                 inner(self, *c, &mut f);
             }
@@ -677,7 +372,7 @@ impl<S: State> RealDom<S> {
             }
         }
         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() {
                 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> {
     type Output = Node<S>;
 
     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> {
     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);
     }
 }
+
+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 dioxus_core::VNode;
+use dioxus_core::ElementId;
+use fxhash::FxHashSet;
 
 use crate::node_ref::{NodeMask, NodeView};
+use crate::traversable::Traversable;
 
 pub(crate) fn union_ordered_iter<T: Ord + Debug>(
     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.
     /// This is sometimes nessisary for lifetime purposes.
     type Ctx;
-    /// This must be either a [ChildDepState] or [NodeDepState]
+    /// This must be either a [ChildDepState], or [NodeDepState]
     type DepState;
     const NODE_MASK: NodeMask = NodeMask::NONE;
     fn reduce<'a>(
@@ -73,70 +71,77 @@ pub trait ParentDepState {
     /// This must be either a [ParentDepState] or [NodeDepState]
     type DepState;
     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.
 /// Called when the current node's node properties are modified or a sibling's [NodeDepState] is modified.
 /// 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 DepState: NodeDepState;
     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 {
-    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,
-        parent: Option<&Self>,
         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
@@ -159,44 +164,14 @@ impl ChildDepState for () {
 impl ParentDepState for () {
     type Ctx = ();
     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
     }
 }
 
-impl NodeDepState for () {
+impl NodeDepState<()> for () {
     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
     }
 }
-
-#[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,
 }
 
-impl NodeDepState for Focus {
+impl NodeDepState<()> for Focus {
     type Ctx = ();
-    type DepState = ();
     const NODE_MASK: NodeMask =
         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 {
             level: if let Some(a) = node.attributes().find(|a| a.name == "tabindex") {
                 if let Some(index) = a

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

@@ -253,7 +253,7 @@ impl InnerInputState {
                 let mut parent = node.parent;
                 while let Some(parent_id) = parent {
                     will_bubble.insert(parent_id);
-                    parent = dom[parent_id.0].parent;
+                    parent = dom[parent_id].parent;
                 }
                 resolved_events.push(UserEvent {
                     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 mut any_map = AnyMap::new();
         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(
@@ -133,7 +133,8 @@ fn render_vdom(
                 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;
 
             loop {
@@ -153,7 +154,7 @@ fn render_vdom(
                     fn resize(dims: Rect, taffy: &mut Taffy, rdom: &Dom) {
                         let width = dims.width;
                         let height = dims.height;
-                        let root_node = rdom[0].state.layout.node.unwrap();
+                        let root_node = rdom[ElementId(0)].state.layout.node.unwrap();
 
                         taffy
                             .compute_layout(
@@ -170,7 +171,7 @@ fn render_vdom(
                             let rdom = rdom.borrow();
                             // size is guaranteed to not change when rendering
                             resize(frame.size(), &mut taffy.borrow_mut(), &rdom);
-                            let root = &rdom[0];
+                            let root = &rdom[ElementId(0)];
                             render::render_vnode(
                                 frame,
                                 &taffy.borrow(),
@@ -249,7 +250,7 @@ fn render_vdom(
                     // update the style and layout
                     let mut any_map = AnyMap::new();
                     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 DepState = ();
-
     const NODE_MASK: dioxus_native_core::node_ref::NodeMask =
         dioxus_native_core::node_ref::NodeMask::new_with_attrs(
             dioxus_native_core::node_ref::AttributeMask::Static(&sorted_str_slice!([
@@ -60,7 +58,7 @@ impl NodeDepState for PreventDefault {
     fn reduce(
         &mut self,
         node: dioxus_native_core::node_ref::NodeView,
-        _sibling: &Self::DepState,
+        _sibling: (),
         _ctx: &Self::Ctx,
     ) -> bool {
         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.
 /// Provided as a root context for all tui applictions.
 /// # Example
-/// ```rust
+/// ```rust, ignore
 /// use dioxus::prelude::*;
 /// use dioxus::tui::query::Query;
 /// use dioxus::tui::Size;

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

@@ -78,7 +78,7 @@ pub(crate) fn render_vnode(
             }
 
             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!(),