implement suspense using simpler approach
This commit is contained in:
5 files changed
+27
-83
No files matched your search
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
Reference in new issue
Block a user