Переглянути джерело

wip: more work on deadline, support immediates

Jonathan Kelley 3 роки тому
батько
коміт
5965bee
1 змінених файлів з 59 додано та 88 видалено
  1. 59 88
      packages/core/src/virtual_dom.rs

+ 59 - 88
packages/core/src/virtual_dom.rs

@@ -18,18 +18,12 @@
 //!
 //! This module includes just the barebones for a complete VirtualDOM API.
 //! Additional functionality is defined in the respective files.
-use futures_util::{Future, StreamExt};
-use fxhash::FxHashMap;
-
-use crate::hooks::{SuspendedContext, SuspenseHook};
 use crate::innerlude::*;
-
-use std::any::Any;
-
-use std::any::TypeId;
-use std::cell::{Ref, RefCell, RefMut};
-use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
-use std::pin::Pin;
+use futures_util::Future;
+use std::{
+    any::{Any, TypeId},
+    pin::Pin,
+};
 
 /// An integrated virtual node system that progresses events and diffs UI trees.
 /// Differences are converted into patches which a renderer can use to draw the UI.
@@ -63,69 +57,50 @@ pub struct VirtualDom {
 }
 
 impl VirtualDom {
-    /// 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.
-    ///
-    /// As an end-user, you'll want to use the Renderer's "new" method instead of this method.
-    /// Directly creating the VirtualDOM is only useful when implementing a new renderer.
+    /// Create a new VirtualDOM with a component that does not have special props.
     ///
+    /// # Description
     ///
-    /// ```ignore
-    /// // Directly from a closure
-    ///
-    /// let dom = VirtualDom::new(|cx| cx.render(rsx!{ div {"hello world"} }));
-    ///
-    /// // or pass in...
+    /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
     ///
-    /// let root = |cx| {
-    ///     cx.render(rsx!{
-    ///         div {"hello world"}
-    ///     })
-    /// }
-    /// let dom = VirtualDom::new(root);
+    /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
+    /// to toss out the entire tree.
     ///
-    /// // or directly from a fn
     ///
-    /// fn Example(cx: Context<()>) -> DomTree  {
+    /// # Example
+    /// ```
+    /// fn Example(cx: Context<SomeProps>) -> VNode  {
     ///     cx.render(rsx!{ div{"hello world"} })
     /// }
     ///
     /// let dom = VirtualDom::new(Example);
     /// ```
+    ///
+    /// Note: the VirtualDOM is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
     pub fn new(root: FC<()>) -> Self {
         Self::new_with_props(root, ())
     }
 
-    /// Start a new VirtualDom instance with a dependent cx.
+    /// Create a new VirtualDOM with the given properties for the root component.
+    ///
+    /// # Description
+    ///
     /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
     ///
     /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
     /// to toss out the entire tree.
     ///
-    /// ```ignore
-    /// // Directly from a closure
-    ///
-    /// let dom = VirtualDom::new(|cx| cx.render(rsx!{ div {"hello world"} }));
     ///
-    /// // or pass in...
-    ///
-    /// let root = |cx| {
-    ///     cx.render(rsx!{
-    ///         div {"hello world"}
-    ///     })
-    /// }
-    /// let dom = VirtualDom::new(root);
-    ///
-    /// // or directly from a fn
-    ///
-    /// fn Example(cx: Context, props: &SomeProps) -> VNode  {
+    /// # Example
+    /// ```
+    /// fn Example(cx: Context<SomeProps>) -> VNode  {
     ///     cx.render(rsx!{ div{"hello world"} })
     /// }
     ///
     /// let dom = VirtualDom::new(Example);
     /// ```
+    ///
+    /// Note: the VirtualDOM is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
     pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
         let components = SharedResources::new();
 
@@ -150,7 +125,7 @@ impl VirtualDom {
 
     pub fn launch_in_place(root: FC<()>) -> Self {
         let mut s = Self::new(root);
-        s.rebuild_in_place().unwrap();
+        s.rebuild().unwrap();
         s
     }
 
@@ -158,7 +133,7 @@ impl VirtualDom {
     ///
     pub fn launch_with_props_in_place<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
         let mut s = Self::new_with_props(root, root_props);
-        s.rebuild_in_place().unwrap();
+        s.rebuild().unwrap();
         s
     }
 
@@ -170,23 +145,6 @@ impl VirtualDom {
         unsafe { self.shared.get_scope(id) }
     }
 
-    /// Rebuilds the VirtualDOM from scratch, but uses a "dummy" RealDom.
-    ///
-    /// Used in contexts where a real copy of the  structure doesn't matter, and the VirtualDOM is the source of truth.
-    ///
-    /// ## Why?
-    ///
-    /// This method uses the `DebugDom` under the hood - essentially making the VirtualDOM's diffing patches a "no-op".
-    ///
-    /// SSR takes advantage of this by using Dioxus itself as the source of truth, and rendering from the tree directly.
-    pub fn rebuild_in_place(&mut self) -> Result<Vec<DomEdit>> {
-        todo!();
-        // let mut realdom = DebugDom::new();
-        // let mut edits = Vec::new();
-        // self.rebuild(&mut realdom, &mut edits)?;
-        // Ok(edits)
-    }
-
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
     ///
     /// The diff machine expects the RealDom's stack to be the root of the application
@@ -219,11 +177,13 @@ impl VirtualDom {
 
     /// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
     ///
-    /// This method will not wait for any suspended tasks, completely skipping over
+    /// This method will not wait for any suspended nodes to complete.
     pub fn run_immediate<'s>(&'s mut self) -> Result<Mutations<'s>> {
-        //
-
-        todo!()
+        use futures_util::FutureExt;
+        let mut is_ready = || false;
+        self.run_with_deadline_and_is_ready(futures_util::future::ready(()), &mut is_ready)
+            .now_or_never()
+            .expect("this future will always resolve immediately")
     }
 
     /// Runs the virtualdom with no time limit.
@@ -281,6 +241,28 @@ impl VirtualDom {
     pub async fn run_with_deadline<'s>(
         &'s mut self,
         mut deadline: impl Future<Output = ()>,
+    ) -> Result<Mutations<'s>> {
+        // let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
+
+        // Configure our deadline
+        use futures_util::FutureExt;
+        let mut deadline_future = deadline.shared();
+
+        let mut is_ready_deadline = deadline_future.clone();
+        let mut is_ready = || -> bool { (&mut is_ready_deadline).now_or_never().is_some() };
+
+        todo!()
+        // Ok(diff_machine.mutations)
+    }
+
+    /// Runs the virtualdom with a deadline and a custom "check" function.
+    ///
+    /// Designed this way so "run_immediate" can re-use all the same rendering logic as "run_with_deadline" but the work
+    /// queue is completely drained;
+    async fn run_with_deadline_and_is_ready<'s>(
+        &'s mut self,
+        mut deadline_future: impl Future<Output = ()>,
+        mut is_ready: &mut impl FnMut() -> bool,
     ) -> Result<Mutations<'s>> {
         /*
         Strategy:
@@ -324,15 +306,6 @@ impl VirtualDom {
         and listeners hold references to hook data, it is wrong to run a scope that is already being diffed.
         */
 
-        // let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
-
-        // Configure our deadline
-        use futures_util::FutureExt;
-        let mut deadline_future = deadline.shared();
-
-        let mut is_ready_deadline = deadline_future.clone();
-        let mut is_ready = || -> bool { (&mut is_ready_deadline).now_or_never().is_some() };
-
         loop {
             // 1. Drain the existing immediates.
             //
@@ -427,9 +400,13 @@ impl VirtualDom {
                 }
             }
 
+            // todo: for these, make these methods return results where deadlinereached is an error type
+
             // 3. Work through the fibers, and wait for any future work to be ready
             let mut machine = DiffMachine::new_headless(&self.shared);
-            self.scheduler.progress_work(&mut machine, &mut is_ready);
+            if self.scheduler.progress_work(&mut machine, &mut is_ready) {
+                break;
+            }
 
             // 4. Wait for any new triggers before looping again
             if self
@@ -440,18 +417,12 @@ impl VirtualDom {
                 break;
             }
         }
-
         todo!()
-        // Ok(diff_machine.mutations)
     }
 
     pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<EventTrigger> {
         self.shared.ui_event_sender.clone()
     }
-
-    fn get_scope_mut(&mut self, id: ScopeId) -> Option<&mut Scope> {
-        unsafe { self.shared.get_scope_mut(id) }
-    }
 }
 
 // TODO!