Merge pull request #2799 from leptos-rs/2798

fix: ProtectedRoute calling view multiple times during server rendering
This commit is contained in:
Greg Johnston 2024-08-09 10:27:10 -04:00 committed by GitHub
commit 693861434c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 103 additions and 2 deletions

View file

@ -442,3 +442,101 @@ where
})
}
}
/// A wrapper that prevents [`Suspense`] from waiting for any resource reads that happen inside
/// `Unsuspend`.
pub struct Unsuspend<T>(Box<dyn FnOnce() -> T + Send>);
impl<T> Unsuspend<T> {
/// Wraps the given function, such that it is not called until all resources are ready.
pub fn new(fun: impl FnOnce() -> T + Send + 'static) -> Self {
Self(Box::new(fun))
}
}
impl<T, Rndr> Render<Rndr> for Unsuspend<T>
where
T: Render<Rndr>,
Rndr: Renderer,
{
type State = T::State;
fn build(self) -> Self::State {
(self.0)().build()
}
fn rebuild(self, state: &mut Self::State) {
(self.0)().rebuild(state);
}
}
impl<T, Rndr> AddAnyAttr<Rndr> for Unsuspend<T>
where
T: AddAnyAttr<Rndr> + 'static,
Rndr: Renderer,
{
type Output<SomeNewAttr: Attribute<Rndr>> =
Unsuspend<T::Output<SomeNewAttr::CloneableOwned>>;
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>,
{
let attr = attr.into_cloneable_owned();
Unsuspend::new(move || (self.0)().add_any_attr(attr))
}
}
impl<T, Rndr> RenderHtml<Rndr> for Unsuspend<T>
where
T: RenderHtml<Rndr> + 'static,
Rndr: Renderer,
{
type AsyncOutput = Self;
const MIN_LENGTH: usize = T::MIN_LENGTH;
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(
self,
buf: &mut String,
position: &mut Position,
escape: bool,
mark_branches: bool,
) {
(self.0)().to_html_with_buf(buf, position, escape, mark_branches);
}
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
self,
buf: &mut StreamBuilder,
position: &mut Position,
escape: bool,
mark_branches: bool,
) where
Self: Sized,
{
(self.0)().to_html_async_with_buf::<OUT_OF_ORDER>(
buf,
position,
escape,
mark_branches,
);
}
fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &Cursor<Rndr>,
position: &PositionState,
) -> Self::State {
(self.0)().hydrate::<FROM_SERVER>(cursor, position)
}
}

View file

@ -331,12 +331,15 @@ where
(view! {
<Transition>
{move || {
match condition() {
let condition = condition();
let view = view.clone();
let redirect_path = redirect_path.clone();
Unsuspend::new(move || match condition {
Some(true) => Either::Left(view()),
#[allow(clippy::unit_arg)]
Some(false) => Either::Right(view! { <Redirect path=redirect_path()/> }),
None => Either::Right(()),
}
})
}}
</Transition>