ソースを参照

wip: cleanup public apis

Jonathan Kelley 3 年 前
コミット
927b05f

+ 0 - 3
packages/core/Cargo.toml

@@ -16,9 +16,6 @@ dioxus-core-macro = { path = "../core-macro", version = "0.1.1" }
 # Bumpalo is used as a micro heap backing each component
 bumpalo = { version = "3.6.0", features = ["collections", "boxed"] }
 
-# custom error type
-thiserror = "1"
-
 # faster hashmaps
 fxhash = "0.2.1"
 

+ 0 - 39
packages/core/README.md

@@ -40,42 +40,3 @@ We have big goals for Dioxus. The final implementation must:
 - Be "live". Components should be able to be both server rendered and client rendered without needing frontend APIs.
 - Be modular. Components and hooks should be work anywhere without worrying about target platform.
 
-## Optimizations
-
-- Support a pluggable allocation strategy that makes VNode creation **very** fast
-- Support lazy VNodes (ie VNodes that are not actually created when the html! macro is used)
-- Support advanced diffing strategies (patience, Meyers, etc)
-
-```rust
-
-rsx!{ "this is a text node" }
-
-rsx!{
-    div {}
-    "asd"
-    div {}
-    div {}
-}
-rsx!{
-    div {
-        a {}
-        b {}
-        c {}
-        Container {
-            Container {
-                Container {
-                    Container {
-                        Container {
-                            div {}
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-
-
-
-```

+ 0 - 78
packages/core/src/debug_renderer.rs

@@ -1,78 +0,0 @@
-//! Debug virtual doms!
-//! This renderer comes built in with dioxus core and shows how to implement a basic renderer.
-//!
-//! Renderers don't actually need to own the virtual dom (it's up to the implementer).
-
-use crate::innerlude::RealDom;
-use crate::{events::EventTrigger, virtual_dom::VirtualDom};
-use crate::{innerlude::Result, prelude::*};
-
-pub struct DebugRenderer {
-    internal_dom: VirtualDom,
-}
-
-impl DebugRenderer {
-    /// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
-    ///
-    /// This means that the root component must either consumes its own context, or statics are used to generate the page.
-    /// The root component can access things like routing in its context.
-    pub fn new(root: FC<()>) -> Self {
-        Self::new_with_props(root, ())
-    }
-
-    /// Create a new text-renderer instance from a functional component root.
-    /// Automatically progresses the creation of the VNode tree to completion.
-    ///
-    /// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
-    pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
-        Self::from_vdom(VirtualDom::new_with_props(root, root_props))
-    }
-
-    /// Create a new text renderer from an existing Virtual DOM.
-    pub fn from_vdom(dom: VirtualDom) -> Self {
-        // todo: initialize the event registry properly
-        Self { internal_dom: dom }
-    }
-
-    pub fn handle_event(&mut self, trigger: EventTrigger) -> Result<()> {
-        Ok(())
-    }
-
-    // pub fn step<Dom: RealDom>(&mut self, machine: &mut DiffMachine<Dom>) -> Result<()> {
-    //     Ok(())
-    // }
-
-    // this does a "holy" compare - if something is missing in the rhs, it doesn't complain.
-    // it only complains if something shows up that's not in the lhs, *or* if a value is different.
-    // This lets you exclude various fields if you just want to drill in to a specific prop
-    // It leverages the internal diffing mechanism.
-    // If you have a list or "nth" child, you do need to list those children, but you don't need to
-    // fill in their children/attrs/etc
-    // Does not handle children or lifecycles and will always fail the test if they show up in the rhs
-    pub fn compare<F>(&self, other: LazyNodes<F>) -> Result<()>
-    where
-        F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
-    {
-        Ok(())
-    }
-
-    // Do a full compare - everything must match
-    // Ignores listeners and children components
-    pub fn compare_full<F>(&self, other: LazyNodes<F>) -> Result<()>
-    where
-        F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
-    {
-        Ok(())
-    }
-
-    pub fn trigger_listener(&mut self, id: usize) -> Result<()> {
-        Ok(())
-    }
-
-    pub fn render_nodes<F>(&self, other: LazyNodes<F>) -> Result<()>
-    where
-        F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
-    {
-        Ok(())
-    }
-}

+ 23 - 38
packages/core/src/diff.rs

@@ -105,7 +105,7 @@ use DomEdit::*;
 ///
 /// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's
 /// stack machines all the way down!
-pub struct DiffMachine<'bump> {
+pub(crate) struct DiffMachine<'bump> {
     pub vdom: &'bump ResourcePool,
     pub mutations: Mutations<'bump>,
     pub stack: DiffStack<'bump>,
@@ -115,7 +115,7 @@ pub struct DiffMachine<'bump> {
 /// a "saved" form of a diff machine
 /// in regular diff machine, the &'bump reference is a stack borrow, but the
 /// bump lifetimes are heap borrows.
-pub struct SavedDiffWork<'bump> {
+pub(crate) struct SavedDiffWork<'bump> {
     pub mutations: Mutations<'bump>,
     pub stack: DiffStack<'bump>,
     pub seen_scopes: FxHashSet<ScopeId>,
@@ -174,36 +174,23 @@ impl<'bump> DiffMachine<'bump> {
     ///
     /// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
     pub async fn work(&mut self) {
-        // defer to individual functions so the compiler produces better code
-        // large functions tend to be difficult for the compiler to work with
         while let Some(instruction) = self.stack.pop() {
+            // defer to individual functions so the compiler produces better code
+            // large functions tend to be difficult for the compiler to work with
             match instruction {
                 DiffInstruction::PopScope => {
                     self.stack.pop_scope();
                 }
 
-                DiffInstruction::DiffNode { old, new, .. } => {
-                    self.diff_node(old, new);
-                }
+                DiffInstruction::DiffNode { old, new, .. } => self.diff_node(old, new),
 
-                DiffInstruction::DiffChildren { old, new } => {
-                    self.diff_children(old, new);
-                }
+                DiffInstruction::DiffChildren { old, new } => self.diff_children(old, new),
 
-                DiffInstruction::Create { node } => {
-                    self.create_node(node);
-                }
+                DiffInstruction::Create { node } => self.create_node(node),
 
-                DiffInstruction::Mount { and } => {
-                    self.mount(and);
-                }
+                DiffInstruction::Mount { and } => self.mount(and),
 
-                DiffInstruction::PrepareMoveNode { node } => {
-                    for el in RealChildIterator::new(node, self.vdom) {
-                        self.mutations.push_root(el.direct_id());
-                        self.stack.add_child_count(1);
-                    }
-                }
+                DiffInstruction::PrepareMoveNode { node } => self.prepare_move_node(node),
             };
 
             // todo: call this less frequently, there is a bit of overhead involved
@@ -211,6 +198,13 @@ impl<'bump> DiffMachine<'bump> {
         }
     }
 
+    fn prepare_move_node(&mut self, node: &'bump VNode<'bump>) {
+        for el in RealChildIterator::new(node, self.vdom) {
+            self.mutations.push_root(el.direct_id());
+            self.stack.add_child_count(1);
+        }
+    }
+
     fn mount(&mut self, and: MountType<'bump>) {
         let nodes_created = self.stack.pop_nodes_created();
         match and {
@@ -363,22 +357,12 @@ impl<'bump> DiffMachine<'bump> {
         let new_component = self.vdom.get_scope_mut(new_idx).unwrap();
 
         // Run the scope for one iteration to initialize it
-        match new_component.run_scope() {
-            Ok(_g) => {
-                // all good, new nodes exist
-            }
-            Err(err) => {
-                // failed to run. this is the first time the component ran, and it failed
-                // we manually set its head node to an empty fragment
-                panic!("failing components not yet implemented");
-            }
+        if new_component.run_scope() {
+            // Take the node that was just generated from running the component
+            let nextnode = new_component.frames.fin_head();
+            self.stack.create_component(new_idx, nextnode);
         }
 
-        // Take the node that was just generated from running the component
-        let nextnode = new_component.frames.fin_head();
-
-        self.stack.create_component(new_idx, nextnode);
-
         // Finally, insert this scope as a seen node.
         self.seen_scopes.insert(new_idx);
     }
@@ -545,8 +529,9 @@ impl<'bump> DiffMachine<'bump> {
                 }
                 false => {
                     // the props are different...
-                    scope.run_scope().unwrap();
-                    self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
+                    if scope.run_scope() {
+                        self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
+                    }
                 }
             }
 

+ 1 - 1
packages/core/src/diff_stack.rs

@@ -40,7 +40,7 @@ pub enum MountType<'a> {
     InsertBefore { other_node: &'a VNode<'a> },
 }
 
-pub struct DiffStack<'bump> {
+pub(crate) struct DiffStack<'bump> {
     instructions: Vec<DiffInstruction<'bump>>,
     nodes_created_stack: SmallVec<[usize; 10]>,
     pub scope_stack: SmallVec<[ScopeId; 5]>,

+ 0 - 105
packages/core/src/dom_edits.rs

@@ -1,105 +0,0 @@
-//!
-//!
-//!
-//!
-//!
-//!
-
-use crate::innerlude::ScopeId;
-
-/// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the
-/// network or through FFI boundaries.
-#[derive(Debug, PartialEq)]
-#[cfg_attr(
-    feature = "serialize",
-    derive(serde::Serialize, serde::Deserialize),
-    serde(tag = "type")
-)]
-pub enum DomEdit<'bump> {
-    PushRoot {
-        id: u64,
-    },
-    PopRoot,
-
-    AppendChildren {
-        many: u32,
-    },
-
-    // "Root" refers to the item direclty
-    // it's a waste of an instruction to push the root directly
-    ReplaceWith {
-        root: u64,
-        m: u32,
-    },
-    InsertAfter {
-        root: u64,
-        n: u32,
-    },
-    InsertBefore {
-        root: u64,
-        n: u32,
-    },
-    Remove {
-        root: u64,
-    },
-
-    RemoveAllChildren,
-    CreateTextNode {
-        text: &'bump str,
-        id: u64,
-    },
-    CreateElement {
-        tag: &'bump str,
-        id: u64,
-    },
-    CreateElementNs {
-        tag: &'bump str,
-        id: u64,
-        ns: &'static str,
-    },
-    CreatePlaceholder {
-        id: u64,
-    },
-    NewEventListener {
-        event_name: &'static str,
-        scope: ScopeId,
-        mounted_node_id: u64,
-    },
-    RemoveEventListener {
-        event: &'static str,
-    },
-    SetText {
-        text: &'bump str,
-    },
-    SetAttribute {
-        field: &'static str,
-        value: &'bump str,
-        ns: Option<&'bump str>,
-    },
-    RemoveAttribute {
-        name: &'static str,
-    },
-}
-impl DomEdit<'_> {
-    pub fn is(&self, id: &'static str) -> bool {
-        match self {
-            DomEdit::InsertAfter { .. } => id == "InsertAfter",
-            DomEdit::InsertBefore { .. } => id == "InsertBefore",
-            DomEdit::PushRoot { .. } => id == "PushRoot",
-            DomEdit::PopRoot => id == "PopRoot",
-            DomEdit::AppendChildren { .. } => id == "AppendChildren",
-            DomEdit::ReplaceWith { .. } => id == "ReplaceWith",
-            DomEdit::Remove { .. } => id == "Remove",
-            DomEdit::RemoveAllChildren => id == "RemoveAllChildren",
-            DomEdit::CreateTextNode { .. } => id == "CreateTextNode",
-            DomEdit::CreateElement { .. } => id == "CreateElement",
-            DomEdit::CreateElementNs { .. } => id == "CreateElementNs",
-            DomEdit::CreatePlaceholder { .. } => id == "CreatePlaceholder",
-            DomEdit::NewEventListener { .. } => id == "NewEventListener",
-            DomEdit::RemoveEventListener { .. } => id == "RemoveEventListener",
-            DomEdit::SetText { .. } => id == "SetText",
-            DomEdit::SetAttribute { .. } => id == "SetAttribute",
-            DomEdit::RemoveAttribute { .. } => id == "RemoveAttribute",
-        }
-    }
-}

+ 0 - 30
packages/core/src/error.rs

@@ -1,30 +0,0 @@
-//! Internal error handling for Dioxus
-//!
-//!
-
-use thiserror::Error as ThisError;
-pub type Result<T, E = Error> = std::result::Result<T, E>;
-
-#[derive(ThisError, Debug)]
-pub enum Error {
-    #[error("Fatal Internal Error: {0}")]
-    FatalInternal(&'static str),
-
-    #[error("Context is missing")]
-    MissingSharedContext,
-
-    #[error("No event to progress")]
-    NoEvent,
-
-    #[error("Wrong Properties Type")]
-    WrongProps,
-
-    #[error("The component failed to return VNodes")]
-    ComponentFailed,
-
-    #[error("Base scope has not been mounted yet")]
-    NotMounted,
-
-    #[error("I/O Error: {0}")]
-    IO(#[from] std::io::Error),
-}

+ 13 - 16
packages/core/src/lib.rs

@@ -1,14 +1,5 @@
 #![allow(non_snake_case)]
 #![doc = include_str!("../README.md")]
-//! Dioxus Core
-//! ----------
-//!
-//!
-//!
-//!
-//!
-//!
-//!
 
 pub use crate::innerlude::{
     format_args_f, html, rsx, Context, DiffInstruction, DioxusElement, DomEdit, DomTree, ElementId,
@@ -26,7 +17,6 @@ pub mod prelude {
     pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
 }
 
-// types used internally that are important
 pub(crate) mod innerlude {
     pub use crate::bumpframe::*;
     pub use crate::childiter::*;
@@ -34,8 +24,6 @@ pub(crate) mod innerlude {
     pub use crate::context::*;
     pub use crate::diff::*;
     pub use crate::diff_stack::*;
-    pub use crate::dom_edits::*;
-    pub use crate::error::*;
     pub use crate::events::*;
     pub use crate::heuristics::*;
     pub use crate::hooklist::*;
@@ -43,7 +31,6 @@ pub(crate) mod innerlude {
     pub use crate::mutations::*;
     pub use crate::nodes::*;
     pub use crate::scheduler::*;
-    // pub use crate::scheduler::*;
     pub use crate::scope::*;
     pub use crate::util::*;
     pub use crate::virtual_dom::*;
@@ -57,19 +44,29 @@ pub(crate) mod innerlude {
 
 pub mod exports {
     //! Important dependencies that are used by the rest of the library
-
     // the foundation of this library
     pub use bumpalo;
 }
 
+/*
+Navigating this crate:
+- virtual_dom: the primary entrypoint for the crate
+- scheduler: the core interior logic called by virtual_dom
+- nodes: the definition of VNodes, listeners, etc.
+-
+
+
+Some utilities
+
+
+
+*/
 pub mod bumpframe;
 pub mod childiter;
 pub mod component;
 pub mod context;
 pub mod diff;
 pub mod diff_stack;
-pub mod dom_edits;
-pub mod error;
 pub mod events;
 pub mod heuristics;
 pub mod hooklist;

+ 97 - 0
packages/core/src/mutations.rs

@@ -163,3 +163,100 @@ impl<'a> NodeRefMutation<'a> {
             .and_then(|f| f.downcast_mut::<T>())
     }
 }
+
+/// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the
+/// network or through FFI boundaries.
+#[derive(Debug, PartialEq)]
+#[cfg_attr(
+    feature = "serialize",
+    derive(serde::Serialize, serde::Deserialize),
+    serde(tag = "type")
+)]
+pub enum DomEdit<'bump> {
+    PushRoot {
+        id: u64,
+    },
+    PopRoot,
+
+    AppendChildren {
+        many: u32,
+    },
+
+    // "Root" refers to the item direclty
+    // it's a waste of an instruction to push the root directly
+    ReplaceWith {
+        root: u64,
+        m: u32,
+    },
+    InsertAfter {
+        root: u64,
+        n: u32,
+    },
+    InsertBefore {
+        root: u64,
+        n: u32,
+    },
+    Remove {
+        root: u64,
+    },
+
+    RemoveAllChildren,
+    CreateTextNode {
+        text: &'bump str,
+        id: u64,
+    },
+    CreateElement {
+        tag: &'bump str,
+        id: u64,
+    },
+    CreateElementNs {
+        tag: &'bump str,
+        id: u64,
+        ns: &'static str,
+    },
+    CreatePlaceholder {
+        id: u64,
+    },
+    NewEventListener {
+        event_name: &'static str,
+        scope: ScopeId,
+        mounted_node_id: u64,
+    },
+    RemoveEventListener {
+        event: &'static str,
+    },
+    SetText {
+        text: &'bump str,
+    },
+    SetAttribute {
+        field: &'static str,
+        value: &'bump str,
+        ns: Option<&'bump str>,
+    },
+    RemoveAttribute {
+        name: &'static str,
+    },
+}
+impl DomEdit<'_> {
+    pub fn is(&self, id: &'static str) -> bool {
+        match self {
+            DomEdit::InsertAfter { .. } => id == "InsertAfter",
+            DomEdit::InsertBefore { .. } => id == "InsertBefore",
+            DomEdit::PushRoot { .. } => id == "PushRoot",
+            DomEdit::PopRoot => id == "PopRoot",
+            DomEdit::AppendChildren { .. } => id == "AppendChildren",
+            DomEdit::ReplaceWith { .. } => id == "ReplaceWith",
+            DomEdit::Remove { .. } => id == "Remove",
+            DomEdit::RemoveAllChildren => id == "RemoveAllChildren",
+            DomEdit::CreateTextNode { .. } => id == "CreateTextNode",
+            DomEdit::CreateElement { .. } => id == "CreateElement",
+            DomEdit::CreateElementNs { .. } => id == "CreateElementNs",
+            DomEdit::CreatePlaceholder { .. } => id == "CreatePlaceholder",
+            DomEdit::NewEventListener { .. } => id == "NewEventListener",
+            DomEdit::RemoveEventListener { .. } => id == "RemoveEventListener",
+            DomEdit::SetText { .. } => id == "SetText",
+            DomEdit::SetAttribute { .. } => id == "SetAttribute",
+            DomEdit::RemoveAttribute { .. } => id == "RemoveAttribute",
+        }
+    }
+}

+ 11 - 6
packages/core/src/scheduler.rs

@@ -128,7 +128,12 @@ pub enum SchedulerMsg {
 /// We can prevent this safety issue from occurring if we track which scopes are invalidated when starting a new task.
 ///
 ///
-pub struct Scheduler {
+pub(crate) struct Scheduler {
+    /// All mounted components are arena allocated to make additions, removals, and references easy to work with
+    /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
+    ///
+    /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
+    /// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
     pub pool: ResourcePool,
 
     pub heuristics: HeuristicsEngine,
@@ -523,7 +528,7 @@ impl Scheduler {
     }
 }
 
-pub struct PriorityLane {
+pub(crate) struct PriorityLane {
     pub dirty_scopes: IndexSet<ScopeId>,
     pub saved_state: Option<SavedDiffWork<'static>>,
     pub in_progress: bool,
@@ -682,7 +687,7 @@ impl ResourcePool {
         &'b self,
         _id: ScopeId,
         _f: impl FnOnce(&'b mut Scope) -> O,
-    ) -> Result<O> {
+    ) -> Option<O> {
         todo!()
     }
 
@@ -692,13 +697,13 @@ impl ResourcePool {
         &self,
         _id: ScopeId,
         _f: impl FnOnce(&mut Scope) -> &VNode<'b>,
-    ) -> Result<&VNode<'b>> {
+    ) -> Option<&VNode<'b>> {
         todo!()
     }
 
-    pub fn try_remove(&self, id: ScopeId) -> Result<Scope> {
+    pub fn try_remove(&self, id: ScopeId) -> Option<Scope> {
         let inner = unsafe { &mut *self.components.get() };
-        Ok(inner.remove(id.0))
+        Some(inner.remove(id.0))
         // .try_remove(id.0)
         // .ok_or_else(|| Error::FatalInternal("Scope not found"))
     }

+ 7 - 10
packages/core/src/scope.rs

@@ -113,7 +113,9 @@ impl Scope {
         self.child_nodes = child_nodes;
     }
 
-    pub(crate) fn run_scope<'sel>(&'sel mut self) -> Result<()> {
+    /// Returns true if the scope completed successfully
+    ///
+    pub(crate) fn run_scope<'sel>(&'sel mut self) -> bool {
         // Cycle to the next frame and then reset it
         // This breaks any latent references, invalidating every pointer referencing into it.
         // Remove all the outdated listeners
@@ -133,17 +135,12 @@ impl Scope {
         let render: &WrappedCaller = self.caller.as_ref();
 
         match render(self) {
-            None => {
-                // the user's component failed. We avoid cycling to the next frame
-                log::error!("Running your component failed! It will no longer receive events.");
-                Err(Error::ComponentFailed)
-            }
+            None => false,
             Some(new_head) => {
                 // the user's component succeeded. We can safely cycle to the next frame
                 self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
                 self.frames.cycle_frame();
-                log::debug!("Successfully rendered component");
-                Ok(())
+                true
             }
         }
     }
@@ -202,7 +199,7 @@ impl Scope {
         &mut self,
         event: SyntheticEvent,
         element: ElementId,
-    ) -> Result<()> {
+    ) -> Option<()> {
         let listners = self.listeners.borrow_mut();
 
         let raw_listener = listners.iter().find(|lis| {
@@ -226,7 +223,7 @@ impl Scope {
             log::warn!("An event was triggered but there was no listener to handle it");
         }
 
-        Ok(())
+        Some(())
     }
 
     pub fn root(&self) -> &VNode {

+ 0 - 19
packages/core/src/util.rs

@@ -11,22 +11,3 @@ pub fn empty_cell() -> Cell<Option<ElementId>> {
 pub fn type_name_of<T>(_: T) -> &'static str {
     std::any::type_name::<T>()
 }
-
-// /// A helper type that lets scopes be ordered by their height
-// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-// pub struct HeightMarker {
-//     pub idx: ScopeId,
-//     pub height: u32,
-// }
-
-// impl Ord for HeightMarker {
-//     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-//         self.height.cmp(&other.height)
-//     }
-// }
-
-// impl PartialOrd for HeightMarker {
-//     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-//         Some(self.cmp(other))
-//     }
-// }

+ 26 - 26
packages/core/src/virtual_dom.rs

@@ -18,6 +18,7 @@
 //!
 //! This module includes just the barebones for a complete VirtualDOM API.
 //! Additional functionality is defined in the respective files.
+
 use crate::innerlude::*;
 use futures_util::{pin_mut, Future, FutureExt};
 use std::{
@@ -35,23 +36,13 @@ use std::{
 ///
 ///
 pub struct VirtualDom {
-    /// All mounted components are arena allocated to make additions, removals, and references easy to work with
-    /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
-    ///
-    /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
-    /// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
-    pub scheduler: Scheduler,
+    scheduler: Scheduler,
 
-    /// The index of the root component
-    /// Should always be the first (gen=0, id=0)
     base_scope: ScopeId,
 
-    // for managing the props that were used to create the dom
-    #[doc(hidden)]
-    _root_prop_type: std::any::TypeId,
+    root_prop_type: std::any::TypeId,
 
-    #[doc(hidden)]
-    _root_props: std::pin::Pin<Box<dyn std::any::Any>>,
+    root_props: Pin<Box<dyn std::any::Any>>,
 }
 
 impl VirtualDom {
@@ -67,8 +58,8 @@ impl VirtualDom {
     ///
     /// # Example
     /// ```
-    /// fn Example(cx: Context<SomeProps>) -> VNode  {
-    ///     cx.render(rsx!{ div{"hello world"} })
+    /// fn Example(cx: Context<()>) -> DomTree  {
+    ///     cx.render(rsx!( div { "hello world" } ))
     /// }
     ///
     /// let dom = VirtualDom::new(Example);
@@ -91,25 +82,34 @@ impl VirtualDom {
     ///
     /// # Example
     /// ```
-    /// fn Example(cx: Context<SomeProps>) -> VNode  {
-    ///     cx.render(rsx!{ div{"hello world"} })
+    /// #[derive(PartialEq, Props)]
+    /// struct SomeProps {
+    ///     name: &'static str
+    /// }
+    ///
+    /// fn Example(cx: Context<SomeProps>) -> DomTree  {
+    ///     cx.render(rsx!{ div{ "hello {cx.name}" } })
     /// }
     ///
     /// let dom = VirtualDom::new(Example);
     /// ```
     ///
-    /// Note: the VirtualDOM is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
+    /// Note: the VirtualDOM is not progressed on creation. You must either "run_with_deadline" or use "rebuild" to progress it.
+    ///
+    /// ```rust
+    /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
+    /// let mutations = dom.rebuild();
+    /// ```
     pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
         let scheduler = Scheduler::new();
 
         let _root_props: Pin<Box<dyn Any>> = Box::pin(root_props);
         let _root_prop_type = TypeId::of::<P>();
 
-        let props_ptr = _root_props.as_ref().downcast_ref::<P>().unwrap() as *const P;
+        let props_ptr = _root_props.downcast_ref::<P>().unwrap() as *const P;
 
         let base_scope = scheduler.pool.insert_scope_with_key(|myidx| {
             let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
-            let name = type_name_of(root);
             Scope::new(
                 caller,
                 myidx,
@@ -122,9 +122,9 @@ impl VirtualDom {
 
         Self {
             base_scope,
-            _root_props,
             scheduler,
-            _root_prop_type,
+            root_props: _root_props,
+            root_prop_type: _root_prop_type,
         }
     }
 
@@ -168,7 +168,7 @@ impl VirtualDom {
             .expect("The base scope should never be moved");
 
         // // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
-        if cur_component.run_scope().is_ok() {
+        if cur_component.run_scope() {
             diff_machine
                 .stack
                 .create_node(cur_component.frames.fin_head(), MountType::Append);
@@ -204,9 +204,9 @@ impl VirtualDom {
             .get_scope_mut(self.base_scope)
             .expect("The base scope should never be moved");
 
-        cur_component.run_scope().unwrap();
-
-        diff_machine.diff_scope(self.base_scope).await;
+        if cur_component.run_scope() {
+            diff_machine.diff_scope(self.base_scope).await;
+        }
 
         diff_machine.mutations
     }