<Suspense/> streaming and hydration issue

This commit is contained in:
Greg Johnston 2022-12-14 23:18:54 -05:00
parent c463579faa
commit 3ef64bd372
3 changed files with 51 additions and 30 deletions

View file

@ -13,6 +13,8 @@ actix-files = { version = "0.6", optional = true }
wasm-bindgen = { version = "0.2", optional = true}
console_error_panic_hook = "0.1.7"
cfg-if = "1.0.0"
gloo-timers = { version = "0.2", features = ["futures"] }
futures = "0.3"
[features]
default = ["ssr"]

View file

@ -3,24 +3,43 @@
use leptos::*;
#[component]
pub fn App(cx: Scope) -> View {
pub fn App(cx: Scope) -> impl IntoView {
let pending_thing = create_resource(
cx,
|| (),
|_| async {
if cfg!(feature = "ssr") {
let (tx, rx) = futures::channel::oneshot::channel();
spawn_local(async {
std::thread::sleep(std::time::Duration::from_millis(500));
tx.send(());
});
rx.await;
} else {
}
()
}
);
view! { cx,
<>
<div>
"This is some text"
</div>
<ComponentA>
<div>"Hello!"</div>
</ComponentA>
<Suspense fallback=move || view! { cx, <p>"Loading..."</p> }>
{move || pending_thing.read().map(|n| view! { cx, <p>"Loaded."</p>})}
</Suspense>
</>
}
}
#[component]
pub fn ComponentA(cx: Scope, children: Box<dyn Fn() -> Vec<View>>) -> View {
pub fn ComponentA(cx: Scope, children: Box<dyn Fn() -> Vec<View>>) -> impl IntoView {
let (value, set_value) = create_signal(cx, "Hello?".to_string());
let (counter, set_counter) = create_signal(cx, 0);
// Test to make sure hydration isn't broken by
// something like this
let _ = [div(cx)].into_view(cx);

View file

@ -1,7 +1,8 @@
use actix_files::Files;
use actix_web::{web, App, HttpResponse, HttpServer};
use actix_web::*;
use hydration_test::*;
use leptos::*;
use futures::StreamExt;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
@ -9,31 +10,30 @@ async fn main() -> std::io::Result<()> {
.service(Files::new("/pkg", "./pkg"))
.route("/", web::get().to(
|| async {
HttpResponse::Ok()
.content_type("text/html")
.body({
let html = render_to_string(|cx|
view! {
cx,
<App/>
}
);
let html = format!(
r#"<!DOCTYPE html>
<html>
<head>
<script type="module">import init from '/pkg/hydration_test.js'; init();</script>
</head>
<body>{html}</body>
</html>"#
);
let pkg_path = "/pkg/hydration_test";
println!("{html}");
html
})
}
)
let head = format!(
r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="modulepreload" href="{pkg_path}.js">
<link rel="preload" href="{pkg_path}_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
<script type="module">import init, {{ start }} from '{pkg_path}.js'; init('{pkg_path}_bg.wasm').then(start);</script>
"#
);
let tail = "</body></html>";
HttpResponse::Ok().content_type("text/html").streaming(
futures::stream::once(async move { head.clone() })
.chain(render_to_stream(
|cx| view! { cx, <App/> }.into_view(cx),
))
.chain(futures::stream::once(async { tail.to_string() }))
.map(|html| Ok(web::Bytes::from(html)) as Result<web::Bytes>),
)})
))
.bind(("127.0.0.1", 8080))?
.run()