From 927b05f358221aa4b9c440608704e86c2400ab64 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 27 Aug 2021 09:40:04 -0400 Subject: [PATCH] wip: cleanup public apis --- packages/core/Cargo.toml | 3 - packages/core/README.md | 39 ----------- packages/core/src/debug_renderer.rs | 78 --------------------- packages/core/src/diff.rs | 61 ++++++---------- packages/core/src/diff_stack.rs | 2 +- packages/core/src/dom_edits.rs | 105 ---------------------------- packages/core/src/error.rs | 30 -------- packages/core/src/lib.rs | 29 ++++---- packages/core/src/mutations.rs | 97 +++++++++++++++++++++++++ packages/core/src/scheduler.rs | 17 +++-- packages/core/src/scope.rs | 17 ++--- packages/core/src/util.rs | 19 ----- packages/core/src/virtual_dom.rs | 52 +++++++------- 13 files changed, 178 insertions(+), 371 deletions(-) delete mode 100644 packages/core/src/debug_renderer.rs delete mode 100644 packages/core/src/dom_edits.rs delete mode 100644 packages/core/src/error.rs diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 96aa0da97..3b3c39c28 100644 --- a/packages/core/Cargo.toml +++ b/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" diff --git a/packages/core/README.md b/packages/core/README.md index 01152bfc9..62a876453 100644 --- a/packages/core/README.md +++ b/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 {} - } - } - } - } - } - } -} - - - - -``` diff --git a/packages/core/src/debug_renderer.rs b/packages/core/src/debug_renderer.rs deleted file mode 100644 index 38604cb1c..000000000 --- a/packages/core/src/debug_renderer.rs +++ /dev/null @@ -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(root: FC, 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(&mut self, machine: &mut DiffMachine) -> 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(&self, other: LazyNodes) -> 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(&self, other: LazyNodes) -> 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(&self, other: LazyNodes) -> Result<()> - where - F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>, - { - Ok(()) - } -} diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 7c6ef2751..3c8b3ebb9 100644 --- a/packages/core/src/diff.rs +++ b/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, @@ -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()); + } } } diff --git a/packages/core/src/diff_stack.rs b/packages/core/src/diff_stack.rs index c09668169..5c4f39646 100644 --- a/packages/core/src/diff_stack.rs +++ b/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>, nodes_created_stack: SmallVec<[usize; 10]>, pub scope_stack: SmallVec<[ScopeId; 5]>, diff --git a/packages/core/src/dom_edits.rs b/packages/core/src/dom_edits.rs deleted file mode 100644 index cbb70f248..000000000 --- a/packages/core/src/dom_edits.rs +++ /dev/null @@ -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", - } - } -} diff --git a/packages/core/src/error.rs b/packages/core/src/error.rs deleted file mode 100644 index 8e675b026..000000000 --- a/packages/core/src/error.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! Internal error handling for Dioxus -//! -//! - -use thiserror::Error as ThisError; -pub type Result = std::result::Result; - -#[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), -} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index d6d4fece6..6b8a2909d 100644 --- a/packages/core/src/lib.rs +++ b/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; diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs index e16126cdc..1eed2c323 100644 --- a/packages/core/src/mutations.rs +++ b/packages/core/src/mutations.rs @@ -163,3 +163,100 @@ impl<'a> NodeRefMutation<'a> { .and_then(|f| f.downcast_mut::()) } } + +/// 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", + } + } +} diff --git a/packages/core/src/scheduler.rs b/packages/core/src/scheduler.rs index 910ba3ea6..9b8ec1a2f 100644 --- a/packages/core/src/scheduler.rs +++ b/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, pub saved_state: Option>, pub in_progress: bool, @@ -682,7 +687,7 @@ impl ResourcePool { &'b self, _id: ScopeId, _f: impl FnOnce(&'b mut Scope) -> O, - ) -> Result { + ) -> Option { 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 { + pub fn try_remove(&self, id: ScopeId) -> Option { 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")) } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 40bfd43ec..081b1bd8f 100644 --- a/packages/core/src/scope.rs +++ b/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 { diff --git a/packages/core/src/util.rs b/packages/core/src/util.rs index c7309fe68..fc406b0a2 100644 --- a/packages/core/src/util.rs +++ b/packages/core/src/util.rs @@ -11,22 +11,3 @@ pub fn empty_cell() -> Cell> { pub fn type_name_of(_: T) -> &'static str { std::any::type_name::() } - -// /// 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 { -// Some(self.cmp(other)) -// } -// } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 6c75899ba..6b152b77b 100644 --- a/packages/core/src/virtual_dom.rs +++ b/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>, + root_props: Pin>, } impl VirtualDom { @@ -67,8 +58,8 @@ impl VirtualDom { /// /// # Example /// ``` - /// fn Example(cx: Context) -> 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) -> VNode { - /// cx.render(rsx!{ div{"hello world"} }) + /// #[derive(PartialEq, Props)] + /// struct SomeProps { + /// name: &'static str + /// } + /// + /// fn Example(cx: Context) -> 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(root: FC

, root_props: P) -> Self { let scheduler = Scheduler::new(); let _root_props: Pin> = Box::pin(root_props); let _root_prop_type = TypeId::of::

(); - let props_ptr = _root_props.as_ref().downcast_ref::

().unwrap() as *const P; + let props_ptr = _root_props.downcast_ref::

().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 }