fix: use separate key in hydration ID for router outlets (closes #1909) (#1939)

This commit is contained in:
Greg Johnston 2023-10-24 15:42:30 -04:00 committed by GitHub
parent d943a50df1
commit db1113e5b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 8 deletions

View file

@ -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<Self, Self::Err> {
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<HydrationKey> = RefCell::new(HydrationKey { fragment: 0, error: 0, id: 0 }));
thread_local!(static ID: RefCell<HydrationKey> = 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,

View file

@ -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::<Memo<crate::RouterState>>();