From db1113e5b3cd24ab77ab26cf1b56eb0cf99046d3 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Tue, 24 Oct 2023 15:42:30 -0400 Subject: [PATCH] fix: use separate key in hydration ID for router outlets (closes #1909) (#1939) --- leptos_dom/src/hydration.rs | 36 ++++++++++++++++++++++++++------- router/src/components/outlet.rs | 2 +- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/leptos_dom/src/hydration.rs b/leptos_dom/src/hydration.rs index 5aca7a425..62fcf2725 100644 --- a/leptos_dom/src/hydration.rs +++ b/leptos_dom/src/hydration.rs @@ -73,6 +73,8 @@ pub(crate) use hydrate_only::*; /// A stable identifier within the server-rendering or hydration process. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct HydrationKey { + /// ID of the current outlet + pub outlet: usize, /// ID of the current fragment. pub fragment: usize, /// ID of the current error boundary. @@ -83,7 +85,11 @@ pub struct HydrationKey { impl Display for HydrationKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}-{}-{}", self.fragment, self.error, self.id) + write!( + f, + "{}-{}-{}-{}", + self.outlet, self.fragment, self.error, self.id + ) } } @@ -91,14 +97,17 @@ impl std::str::FromStr for HydrationKey { type Err = (); // TODO better error fn from_str(s: &str) -> Result { - let mut pieces = s.splitn(3, '-'); + let mut pieces = s.splitn(4, '-'); let first = pieces.next().ok_or(())?; let second = pieces.next().ok_or(())?; let third = pieces.next().ok_or(())?; - let fragment = usize::from_str(first).map_err(|_| ())?; - let error = usize::from_str(second).map_err(|_| ())?; - let id = usize::from_str(third).map_err(|_| ())?; + let fourth = pieces.next().ok_or(())?; + let outlet = usize::from_str(first).map_err(|_| ())?; + let fragment = usize::from_str(second).map_err(|_| ())?; + let error = usize::from_str(third).map_err(|_| ())?; + let id = usize::from_str(fourth).map_err(|_| ())?; Ok(HydrationKey { + outlet, fragment, error, id, @@ -113,8 +122,9 @@ mod tests { use crate::HydrationKey; use std::str::FromStr; assert_eq!( - HydrationKey::from_str("1-2-3"), + HydrationKey::from_str("0-1-2-3"), Ok(HydrationKey { + outlet: 0, fragment: 1, error: 2, id: 3 @@ -123,7 +133,7 @@ mod tests { } } -thread_local!(static ID: RefCell = RefCell::new(HydrationKey { fragment: 0, error: 0, id: 0 })); +thread_local!(static ID: RefCell = RefCell::new(HydrationKey { outlet: 0, fragment: 0, error: 0, id: 0 })); /// Control and utility methods for hydration. pub struct HydrationCtx; @@ -187,6 +197,16 @@ impl HydrationCtx { }) } + /// Resets the hydration `id` for the next outlet, and returns it + pub fn next_outlet() -> HydrationKey { + ID.with(|id| { + let mut id = id.borrow_mut(); + id.outlet = id.outlet.wrapping_add(1); + id.id = 0; + *id + }) + } + /// Resets the hydration `id` for the next component, and returns it pub fn next_error() -> HydrationKey { ID.with(|id| { @@ -202,6 +222,7 @@ impl HydrationCtx { pub fn reset_id() { ID.with(|id| { *id.borrow_mut() = HydrationKey { + outlet: 0, fragment: 0, error: 0, id: 0, @@ -220,6 +241,7 @@ impl HydrationCtx { pub fn continue_after(id: HydrationKey) { ID.with(|i| { *i.borrow_mut() = HydrationKey { + outlet: id.outlet, fragment: id.fragment, error: id.error, id: id.id + 1, diff --git a/router/src/components/outlet.rs b/router/src/components/outlet.rs index 693aac95b..82c3e6999 100644 --- a/router/src/components/outlet.rs +++ b/router/src/components/outlet.rs @@ -15,7 +15,7 @@ use web_sys::AnimationEvent; )] #[component] pub fn Outlet() -> impl IntoView { - _ = HydrationCtx::next_component(); + _ = HydrationCtx::next_outlet(); let id = HydrationCtx::id(); let route = use_route(); let route_states = expect_context::>();