implement suspense using simpler approach

This commit is contained in:
Jonathan Kelley committed 2023-07-14 17:12:07 -07:00
1 parent 81075748f6
commit f0128a3150
5 files changed
+27 -83

No files matched your search

+1
View File
@@ -40,6 +40,7 @@ tokio = { workspace = true, features = ["full"] }
dioxus = { workspace = true } dioxus = { workspace = true }
pretty_assertions = "1.3.0" pretty_assertions = "1.3.0"
rand = "0.8.5" rand = "0.8.5"
dioxus-ssr = { workspace = true }
[features] [features]
default = [] default = []
+2
View File
@@ -85,6 +85,8 @@ impl VirtualDom {
if scope.suspended.get() { if scope.suspended.get() {
self.suspended_scopes.insert(scope.id); self.suspended_scopes.insert(scope.id);
} else if !self.suspended_scopes.is_empty() {
_ = self.suspended_scopes.remove(&scope.id);
} }
// rebind the lifetime now that its stored internally // rebind the lifetime now that its stored internally
+1 -1
View File
@@ -655,7 +655,7 @@ impl<'src> ScopeState {
} }
/// Mark this component as suspended and then return None /// Mark this component as suspended and then return None
pub fn suspend(&self) -> Option<()> { pub fn suspend(&self) -> Option<Element> {
self.suspended.set(true); self.suspended.set(true);
None None
} }
+3 -1
View File
@@ -572,10 +572,12 @@ impl VirtualDom {
pub async fn wait_for_suspense(&mut self) { pub async fn wait_for_suspense(&mut self) {
loop { loop {
if self.suspended_scopes.is_empty() { if self.suspended_scopes.is_empty() {
break; return;
} }
self.wait_for_work().await; self.wait_for_work().await;
_ = self.render_immediate();
} }
} }
+20 -81
View File
@@ -1,103 +1,42 @@
use dioxus::core::ElementId;
use dioxus::core::Mutation::*; use dioxus::core::Mutation::*;
use dioxus::prelude::*; use dioxus::prelude::*;
use std::future::IntoFuture; use std::future::IntoFuture;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
#[test] #[tokio::test]
fn it_works() { async fn it_works() {
// wait just a moment, not enough time for the boundary to resolve // wait just a moment, not enough time for the boundary to resolve
tokio::runtime::Builder::new_current_thread() let mut dom = VirtualDom::new(app);
.enable_time() _ = dom.rebuild();
.build() dom.wait_for_suspense().await;
.unwrap() let out = dioxus_ssr::pre_render(&dom);
.block_on(async {
let mut dom = VirtualDom::new(app);
{ assert_eq!(out, "<div>Waiting for... child</div>");
let mutations = dom.rebuild().santize();
// We should at least get the top-level template in before pausing for the children dbg!(out);
// note: we dont test template edits anymore
// assert_eq!(
// mutations.templates,
// [
// CreateElement { name: "div" },
// CreateStaticText { value: "Waiting for child..." },
// CreateStaticPlaceholder,
// AppendChildren { m: 2 },
// SaveTemplate { name: "template", m: 1 }
// ]
// );
// And we should load it in and assign the placeholder properly
assert_eq!(
mutations.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
// can we even?
AssignId { path: &[1], id: ElementId(3) },
AppendChildren { m: 1, id: ElementId(0) },
]
);
}
dom.wait_for_work().await;
});
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
cx.render(rsx!( cx.render(rsx!(
div { div {
"Waiting for child..." "Waiting for... "
suspense_boundary {} suspended_child {}
} }
)) ))
} }
fn suspense_boundary(cx: Scope) -> Element { fn suspended_child(cx: Scope) -> Element {
todo!() let mut val = use_state(cx, || 0);
// cx.use_hook(|| {
// cx.provide_context(Rc::new(SuspenseContext::new(cx.scope_id())));
// });
// // Ensure the right types are found if **val < 3 {
// cx.has_context::<Rc<SuspenseContext>>().unwrap(); let mut val = val.clone();
cx.spawn(async move {
val += 1;
});
return cx.suspend()?;
}
// cx.render(rsx!(async_child {})) render!("child")
}
fn async_child(cx: Scope<'_>) -> Element {
todo!()
// use_future!(cx, || tokio::time::sleep(Duration::from_millis(10))).await;
// cx.render(rsx!(async_text {}))
}
fn async_text(cx: Scope<'_>) -> Element {
todo!()
// let username = use_future!(cx, || async {
// tokio::time::sleep(std::time::Duration::from_secs(1)).await;
// "async child 1"
// });
// let age = use_future!(cx, || async {
// tokio::time::sleep(std::time::Duration::from_secs(2)).await;
// 1234
// });
// let (_user, _age) = use_future!(cx, || async {
// tokio::join!(
// tokio::time::sleep(std::time::Duration::from_secs(1)),
// tokio::time::sleep(std::time::Duration::from_secs(2))
// );
// ("async child 1", 1234)
// })
// .await;
// let (username, age) = tokio::join!(username.into_future(), age.into_future());
// cx.render(rsx!( div { "Hello! {username}, you are {age}, {_user} {_age}" } ))
} }