diff --git a/leptos/src/suspense_component.rs b/leptos/src/suspense_component.rs index e4720dbe09..ec18d46172 100644 --- a/leptos/src/suspense_component.rs +++ b/leptos/src/suspense_component.rs @@ -2,8 +2,10 @@ use leptos_dom::{DynChild, HydrationCtx, IntoView}; use leptos_macro::component; #[cfg(any(feature = "csr", feature = "hydrate"))] use leptos_reactive::SignalGet; +#[allow(unused)] use leptos_reactive::{ - create_memo, provide_context, SignalGetUntracked, SuspenseContext, + create_memo, provide_context, run_as_child, SignalGetUntracked, + SuspenseContext, }; #[cfg(not(any(feature = "csr", feature = "hydrate")))] use leptos_reactive::{with_owner, Owner, SharedContext}; @@ -75,16 +77,6 @@ where let owner = Owner::current().expect(" created with no reactive owner"); - // provide this SuspenseContext to any resources below it - // run in a memo so the children are children of this parent - let children = create_memo({ - let orig_children = Rc::clone(&orig_children); - move |_| { - provide_context(context); - orig_children().into_view() - } - }); - // likewise for the fallback let fallback = create_memo({ move |_| { @@ -100,13 +92,20 @@ where let child = DynChild::new({ move || { - // pull lazy memo before checking if context is ready - let children_rendered = children.get_untracked(); + // provide this SuspenseContext to any resources below it + // run in a memo so the children are children of this parent + let children = run_as_child({ + let orig_children = Rc::clone(&orig_children); + move || { + provide_context(context); + orig_children().into_view() + } + }); #[cfg(any(feature = "csr", feature = "hydrate"))] { if ready.get() { - children_rendered + children } else { fallback.get_untracked() } @@ -123,8 +122,7 @@ where if context.pending_resources.get() == 0 { with_owner(owner, move || { //HydrationCtx::continue_from(current_id); - DynChild::new(move || children_rendered.clone()) - .into_view() + DynChild::new(move || children.clone()).into_view() }) } // show the fallback, but also prepare to stream HTML diff --git a/leptos_reactive/src/lib.rs b/leptos_reactive/src/lib.rs index 7367ab737c..a7dc6bbf3b 100644 --- a/leptos_reactive/src/lib.rs +++ b/leptos_reactive/src/lib.rs @@ -114,8 +114,8 @@ pub use resource::*; use runtime::*; pub use runtime::{ as_child_of_current_owner, batch, create_runtime, current_runtime, - on_cleanup, set_current_runtime, untrack, untrack_with_diagnostics, - with_current_owner, with_owner, Owner, RuntimeId, + on_cleanup, run_as_child, set_current_runtime, untrack, + untrack_with_diagnostics, with_current_owner, with_owner, Owner, RuntimeId, }; pub use selector::*; pub use serialization::*; diff --git a/leptos_reactive/src/runtime.rs b/leptos_reactive/src/runtime.rs index fcb5f4eab6..6edee94883 100644 --- a/leptos_reactive/src/runtime.rs +++ b/leptos_reactive/src/runtime.rs @@ -680,6 +680,40 @@ where } } +/// Runs the given function as a child of the current Owner, once. +pub fn run_as_child(f: impl FnOnce() -> T + 'static) -> T { + let owner = with_runtime(|runtime| runtime.owner.get()) + .expect("runtime should be alive when created"); + let (value, disposer) = with_runtime(|runtime| { + let prev_observer = runtime.observer.take(); + let prev_owner = runtime.owner.take(); + + runtime.owner.set(owner); + runtime.observer.set(owner); + + let id = runtime.nodes.borrow_mut().insert(ReactiveNode { + value: None, + state: ReactiveNodeState::Clean, + node_type: ReactiveNodeType::Trigger, + }); + runtime.push_scope_property(ScopeProperty::Trigger(id)); + let disposer = Disposer(id); + + runtime.owner.set(Some(id)); + runtime.observer.set(Some(id)); + + let v = f(); + + runtime.observer.set(prev_observer); + runtime.owner.set(prev_owner); + + (v, disposer) + }) + .expect("runtime should be alive when run"); + on_cleanup(move || drop(disposer)); + value +} + /// Wraps the given function so that, whenever it is called, it is run /// in the reactive scope of whatever the reactive owner was when it was /// created.