//! # Virtual DOM Implementation for Rust //! //! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust. use crate::innerlude::Work; use crate::properties::RootProps; use crate::root_wrapper::RootScopeWrapper; use crate::{ arena::ElementId, innerlude::{ ElementRef, NoOpMutations, SchedulerMsg, ScopeOrder, ScopeState, VNodeMount, VProps, WriteMutations, }, nodes::{Template, TemplateId}, runtime::{Runtime, RuntimeGuard}, scopes::ScopeId, AttributeValue, ComponentFunction, Element, Event, Mutations, }; use crate::{Task, VComponent}; use futures_util::StreamExt; use rustc_hash::FxHashMap; use slab::Slab; use std::collections::BTreeSet; use std::{any::Any, rc::Rc}; use tracing::instrument; /// A virtual node system that progresses user events and diffs UI trees. /// /// ## Guide /// /// Components are defined as simple functions that take [`crate::properties::Properties`] and return an [`Element`]. /// /// ```rust /// # use dioxus::prelude::*; /// /// #[derive(Props, PartialEq, Clone)] /// struct AppProps { /// title: String /// } /// /// fn app(cx: AppProps) -> Element { /// rsx!( /// div {"hello, {cx.title}"} /// ) /// } /// ``` /// /// Components may be composed to make complex apps. /// /// ```rust /// # #![allow(unused)] /// # use dioxus::prelude::*; /// /// # #[derive(Props, PartialEq, Clone)] /// # struct AppProps { /// # title: String /// # } /// /// static ROUTES: &str = ""; /// /// #[component] /// fn app(cx: AppProps) -> Element { /// rsx!( /// NavBar { routes: ROUTES } /// Title { "{cx.title}" } /// Footer {} /// ) /// } /// /// #[component] /// fn NavBar( routes: &'static str) -> Element { /// rsx! { /// div { "Routes: {routes}" } /// } /// } /// /// #[component] /// fn Footer() -> Element { /// rsx! { div { "Footer" } } /// } /// /// #[component] /// fn Title( children: Element) -> Element { /// rsx! { /// div { id: "title", {children} } /// } /// } /// ``` /// /// To start an app, create a [`VirtualDom`] and call [`VirtualDom::rebuild`] to get the list of edits required to /// draw the UI. /// /// ```rust /// # use dioxus::prelude::*; /// # use dioxus_core::*; /// # fn app() -> Element { rsx! { div {} } } /// /// let mut vdom = VirtualDom::new(app); /// let edits = vdom.rebuild_to_vec(); /// ``` /// /// To call listeners inside the VirtualDom, call [`VirtualDom::handle_event`] with the appropriate event data. /// /// ```rust, no_run /// # use dioxus::prelude::*; /// # use dioxus_core::*; /// # fn app() -> Element { rsx! { div {} } } /// # let mut vdom = VirtualDom::new(app); /// let event = std::rc::Rc::new(0); /// vdom.handle_event("onclick", event, ElementId(0), true); /// ``` /// /// While no events are ready, call [`VirtualDom::wait_for_work`] to poll any futures inside the VirtualDom. /// /// ```rust, no_run /// # use dioxus::prelude::*; /// # use dioxus_core::*; /// # fn app() -> Element { rsx! { div {} } } /// # let mut vdom = VirtualDom::new(app); /// tokio::runtime::Runtime::new().unwrap().block_on(async { /// vdom.wait_for_work().await; /// }); /// ``` /// /// Once work is ready, call [`VirtualDom::render_immediate`] to compute the differences between the previous and /// current UI trees. This will write edits to a [`WriteMutations`] object you pass in that contains with edits that need to be /// handled by the renderer. /// /// ```rust, no_run /// # use dioxus::prelude::*; /// # use dioxus_core::*; /// # fn app() -> Element { rsx! { div {} } } /// # let mut vdom = VirtualDom::new(app); /// let mut mutations = Mutations::default(); /// /// vdom.render_immediate(&mut mutations); /// ``` /// /// To not wait for suspense while diffing the VirtualDom, call [`VirtualDom::render_immediate`]. /// /// /// ## Building an event loop around Dioxus: /// /// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above. /// ```rust, no_run /// # use dioxus::prelude::*; /// # use dioxus_core::*; /// # struct RealDom; /// # struct Event {} /// # impl RealDom { /// # fn new() -> Self { /// # Self {} /// # } /// # fn apply(&mut self) -> Mutations { /// # unimplemented!() /// # } /// # async fn wait_for_event(&mut self) -> std::rc::Rc { /// # unimplemented!() /// # } /// # } /// # /// # tokio::runtime::Runtime::new().unwrap().block_on(async { /// let mut real_dom = RealDom::new(); /// /// #[component] /// fn app() -> Element { /// rsx! { /// div { "Hello World" } /// } /// } /// /// let mut dom = VirtualDom::new(app); /// /// dom.rebuild(&mut real_dom.apply()); /// /// loop { /// tokio::select! { /// _ = dom.wait_for_work() => {} /// evt = real_dom.wait_for_event() => dom.handle_event("onclick", evt, ElementId(0), true), /// } /// /// dom.render_immediate(&mut real_dom.apply()); /// } /// # }); /// ``` /// /// ## Waiting for suspense /// /// Because Dioxus supports suspense, you can use it for server-side rendering, static site generation, and other use cases /// where waiting on portions of the UI to finish rendering is important. To wait for suspense, use the /// [`VirtualDom::wait_for_suspense`] method: /// /// ```rust, no_run /// # use dioxus::prelude::*; /// # use dioxus_core::*; /// # fn app() -> Element { rsx! { div {} } } /// tokio::runtime::Runtime::new().unwrap().block_on(async { /// let mut dom = VirtualDom::new(app); /// /// dom.rebuild_in_place(); /// dom.wait_for_suspense().await; /// }); /// /// // Render the virtual dom /// ``` pub struct VirtualDom { pub(crate) scopes: Slab, pub(crate) dirty_scopes: BTreeSet, // A map of overridden templates? pub(crate) templates: FxHashMap, // Templates changes that are queued for the next render pub(crate) queued_templates: Vec