From f7053ac96058995ecf60063d24b5a630cad1da0b Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Thu, 8 Aug 2024 13:56:02 -0400 Subject: [PATCH 1/2] feat: add Unsuspend as a way to opt out of Suspense in a section of the tree --- leptos/src/suspense_component.rs | 98 ++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/leptos/src/suspense_component.rs b/leptos/src/suspense_component.rs index 69ade9263..2d3c113bd 100644 --- a/leptos/src/suspense_component.rs +++ b/leptos/src/suspense_component.rs @@ -442,3 +442,101 @@ where }) } } + +/// A wrapper that prevents [`Suspense`] from waiting for any resource reads that happen inside +/// `Unsuspend`. +pub struct Unsuspend(Box T + Send>); + +impl Unsuspend { + /// 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 Render for Unsuspend +where + T: Render, + 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 AddAnyAttr for Unsuspend +where + T: AddAnyAttr + 'static, + Rndr: Renderer, +{ + type Output> = + Unsuspend>; + + fn add_any_attr>( + self, + attr: NewAttr, + ) -> Self::Output + where + Self::Output: RenderHtml, + { + let attr = attr.into_cloneable_owned(); + Unsuspend::new(move || (self.0)().add_any_attr(attr)) + } +} + +impl RenderHtml for Unsuspend +where + T: RenderHtml + '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( + self, + buf: &mut StreamBuilder, + position: &mut Position, + escape: bool, + mark_branches: bool, + ) where + Self: Sized, + { + (self.0)().to_html_async_with_buf::( + buf, + position, + escape, + mark_branches, + ); + } + + fn hydrate( + self, + cursor: &Cursor, + position: &PositionState, + ) -> Self::State { + (self.0)().hydrate::(cursor, position) + } +} From f1fae6306418e68b47c1d2876101b64db3e518dd Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Thu, 8 Aug 2024 13:57:58 -0400 Subject: [PATCH 2/2] fix: only run Suspense on the condition in ProtectedRoute (closes #2798) --- router/src/components.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/router/src/components.rs b/router/src/components.rs index abed6b49a..aaff39477 100644 --- a/router/src/components.rs +++ b/router/src/components.rs @@ -331,12 +331,15 @@ where (view! { {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! { }), None => Either::Right(()), - } + }) }}