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 }
pretty_assertions = "1.3.0"
rand = "0.8.5"
dioxus-ssr = { workspace = true }
[features]
default = []
+2
View File
@@ -85,6 +85,8 @@ impl VirtualDom {
if scope.suspended.get() {
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
+1 -1
View File
@@ -655,7 +655,7 @@ impl<'src> ScopeState {
}
/// Mark this component as suspended and then return None
pub fn suspend(&self) -> Option<()> {
pub fn suspend(&self) -> Option<Element> {
self.suspended.set(true);
None
}
+3 -1
View File
@@ -572,10 +572,12 @@ impl VirtualDom {
pub async fn wait_for_suspense(&mut self) {
loop {
if self.suspended_scopes.is_empty() {
break;
return;
}
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::prelude::*;
use std::future::IntoFuture;
use std::rc::Rc;
use std::time::Duration;
#[test]
fn it_works() {
#[tokio::test]
async fn it_works() {
// wait just a moment, not enough time for the boundary to resolve
tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.unwrap()
.block_on(async {
let mut dom = VirtualDom::new(app);
let mut dom = VirtualDom::new(app);
_ = dom.rebuild();
dom.wait_for_suspense().await;
let out = dioxus_ssr::pre_render(&dom);
{
let mutations = dom.rebuild().santize();
assert_eq!(out, "<div>Waiting for... child</div>");
// We should at least get the top-level template in before pausing for the children
// 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;
});
dbg!(out);
}
fn app(cx: Scope) -> Element {
cx.render(rsx!(
div {
"Waiting for child..."
suspense_boundary {}
"Waiting for... "
suspended_child {}
}
))
}
fn suspense_boundary(cx: Scope) -> Element {
todo!()
// cx.use_hook(|| {
// cx.provide_context(Rc::new(SuspenseContext::new(cx.scope_id())));
// });
fn suspended_child(cx: Scope) -> Element {
let mut val = use_state(cx, || 0);
// // Ensure the right types are found
// cx.has_context::<Rc<SuspenseContext>>().unwrap();
if **val < 3 {
let mut val = val.clone();
cx.spawn(async move {
val += 1;
});
return cx.suspend()?;
}
// cx.render(rsx!(async_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}" } ))
render!("child")
}