Fix <Suspense/> hydration

This commit is contained in:
Greg Johnston 2023-01-14 14:10:19 -05:00
parent e17afd4559
commit 6f95713b59
2 changed files with 17 additions and 47 deletions

View file

@ -1,5 +1,5 @@
use cfg_if::cfg_if;
use leptos_dom::{Component, DynChild, Fragment, IntoView};
use leptos_dom::{DynChild, Fragment, IntoView};
#[cfg(not(any(feature = "csr", feature = "hydrate")))]
use leptos_dom::{HydrationCtx, HydrationKey};
use leptos_macro::component;
@ -73,7 +73,7 @@ where
let orig_child = Rc::new(children);
Component::new("Suspense", move |cx| {
leptos_dom::custom(cx, leptos_dom::Custom::new("leptos-suspense")).child({
#[cfg(not(any(feature = "csr", feature = "hydrate")))]
let current_id = HydrationCtx::peek();
@ -104,13 +104,9 @@ where
&current_id.to_string(),
{
let current_id = current_id.clone();
let fragment_id = HydrationKey {
previous: current_id.previous,
offset: current_id.offset + 1
};
move || {
HydrationCtx::continue_from(fragment_id);
orig_child(cx)
HydrationCtx::continue_from(current_id.clone());
DynChild::new(move || orig_child(cx))
.into_view(cx)
.render_to_string(cx)
.to_string()

View file

@ -149,45 +149,19 @@ pub fn render_to_stream_with_prefix_undisposed(
// resources and fragments
// stream HTML for each <Suspense/> as it resolves
let fragments = fragments.map(|(fragment_id, id_before_suspense, html)| {
cfg_if! {
if #[cfg(debug_assertions)] {
_ = id_before_suspense;
// Debug-mode <Suspense/>-replacement code
format!(
r#"
<template id="{fragment_id}f">{html}</template>
<script>
var start = document.getElementById("_{fragment_id}o");
var end = document.getElementById("_{fragment_id}c");
var range = new Range();
range.setStartBefore(start.nextSibling.nextSibling);
range.setEndAfter(end.previousSibling.previousSibling);
range.deleteContents();
var tpl = document.getElementById("{fragment_id}f");
end.parentNode.insertBefore(tpl.content.cloneNode(true), end.previousSibling);
</script>
"#
)
} else {
// Release-mode <Suspense/>-replacement code
format!(
r#"
<template id="{fragment_id}f">{html}</template>
<script>
var start = document.getElementById("_{id_before_suspense}");
var end = document.getElementById("_{fragment_id}");
var range = new Range();
range.setStartAfter(start);
range.setEndBefore(end);
range.deleteContents();
var tpl = document.getElementById("{fragment_id}f");
end.parentNode.insertBefore(tpl.content.cloneNode(true), end.previousSibling);
</script>
"#
)
}
}
// TODO can remove id_before_suspense entirely now
let fragments = fragments.map(|(fragment_id, _, html)| {
format!(
r#"
<template id="{fragment_id}f">{html}</template>
<script>
var placeholder = document.getElementById("_{fragment_id}");
var tpl = document.getElementById("{fragment_id}f");
placeholder.textContent = "";
placeholder.append(tpl.content.cloneNode(true));
</script>
"#
)
});
// stream data for each Resource as it resolves
let resources = serializers.map(|(id, json)| {