From 83a848b5ec44b2caffc9ba01ebdb9caf1f869b7d Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Mon, 17 Nov 2025 21:00:41 -0500 Subject: [PATCH] chore: clean up up warning behavior for resources that depend on other resources (#4415) (closes #3372) --- leptos_server/src/resource.rs | 43 +++++++++++++++++++++++++++-- reactive_graph/src/effect/effect.rs | 26 +++++++++++------ 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/leptos_server/src/resource.rs b/leptos_server/src/resource.rs index 91289c319..5065931ba 100644 --- a/leptos_server/src/resource.rs +++ b/leptos_server/src/resource.rs @@ -188,6 +188,39 @@ where } } +#[cfg(debug_assertions)] +thread_local! { + static RESOURCE_SOURCE_SIGNAL_ACTIVE: AtomicBool = const { AtomicBool::new(false) }; +} + +#[cfg(debug_assertions)] +/// Returns whether the current thread is currently running a resource source signal. +pub fn in_resource_source_signal() -> bool { + RESOURCE_SOURCE_SIGNAL_ACTIVE + .with(|scope| scope.load(std::sync::atomic::Ordering::Relaxed)) +} + +/// Set a static to true whilst running the given function. +/// [`is_in_effect_scope`] will return true whilst the function is running. +fn run_in_resource_source_signal(fun: impl FnOnce() -> T) -> T { + #[cfg(debug_assertions)] + { + // For the theoretical nested case, set back to initial value rather than false: + let initial = RESOURCE_SOURCE_SIGNAL_ACTIVE.with(|scope| { + scope.swap(true, std::sync::atomic::Ordering::Relaxed) + }); + let result = fun(); + RESOURCE_SOURCE_SIGNAL_ACTIVE.with(|scope| { + scope.store(initial, std::sync::atomic::Ordering::Relaxed) + }); + result + } + #[cfg(not(debug_assertions))] + { + fun() + } +} + impl ReadUntracked for ArcResource where T: 'static, @@ -202,7 +235,9 @@ where computed::suspense::SuspenseContext, effect::in_effect_scope, owner::use_context, }; - if !in_effect_scope() && use_context::().is_none() + if !in_effect_scope() + && !in_resource_source_signal() + && use_context::().is_none() { let location = std::panic::Location::caller(); reactive_graph::log_warning(format_args!( @@ -271,7 +306,7 @@ where let refetch = ArcRwSignal::new(0); let source = ArcMemo::new({ let refetch = refetch.clone(); - move |_| (refetch.get(), source()) + move |_| (refetch.get(), run_in_resource_source_signal(&source)) }); let fun = { let source = source.clone(); @@ -909,7 +944,9 @@ where computed::suspense::SuspenseContext, effect::in_effect_scope, owner::use_context, }; - if !in_effect_scope() && use_context::().is_none() + if !in_effect_scope() + && !in_resource_source_signal() + && use_context::().is_none() { let location = std::panic::Location::caller(); reactive_graph::log_warning(format_args!( diff --git a/reactive_graph/src/effect/effect.rs b/reactive_graph/src/effect/effect.rs index 60da480b9..f05f34b57 100644 --- a/reactive_graph/src/effect/effect.rs +++ b/reactive_graph/src/effect/effect.rs @@ -110,10 +110,12 @@ fn effect_base() -> (Receiver, Owner, Arc>) { (rx, owner, inner) } +#[cfg(debug_assertions)] thread_local! { static EFFECT_SCOPE_ACTIVE: AtomicBool = const { AtomicBool::new(false) }; } +#[cfg(debug_assertions)] /// Returns whether the current thread is currently running an effect. pub fn in_effect_scope() -> bool { EFFECT_SCOPE_ACTIVE @@ -123,14 +125,22 @@ pub fn in_effect_scope() -> bool { /// Set a static to true whilst running the given function. /// [`is_in_effect_scope`] will return true whilst the function is running. fn run_in_effect_scope(fun: impl FnOnce() -> T) -> T { - // For the theoretical nested case, set back to initial value rather than false: - let initial = EFFECT_SCOPE_ACTIVE - .with(|scope| scope.swap(true, std::sync::atomic::Ordering::Relaxed)); - let result = fun(); - EFFECT_SCOPE_ACTIVE.with(|scope| { - scope.store(initial, std::sync::atomic::Ordering::Relaxed) - }); - result + #[cfg(debug_assertions)] + { + // For the theoretical nested case, set back to initial value rather than false: + let initial = EFFECT_SCOPE_ACTIVE.with(|scope| { + scope.swap(true, std::sync::atomic::Ordering::Relaxed) + }); + let result = fun(); + EFFECT_SCOPE_ACTIVE.with(|scope| { + scope.store(initial, std::sync::atomic::Ordering::Relaxed) + }); + result + } + #[cfg(not(debug_assertions))] + { + fun() + } } impl Effect