Unify the warning system (#2649)

* unify the warning system

* fix VirtualDom::new warning with a component

* move warnings to dioxuslabs

* also allow writes in the component body when converting from T -> ReadOnlySignal<T>

* fix clippy from merge conflict

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2024-07-24 15:50:36 -07:00
1 parent f8cb07e673
commit a6c025c8ef
10 files changed
+132 -63

No files matched your search

+7 -1
View File
@@ -46,7 +46,13 @@ impl<T: 'static, S: Storage<SignalData<T>>> ReadOnlySignal<T, S> {
/// This should only be used by the `rsx!` macro.
pub fn __set(&mut self, value: T) {
use crate::write::Writable;
self.inner.set(value);
use warnings::Warning;
// This is only called when converting T -> ReadOnlySignal<T> which will not cause loops
crate::warnings::signal_write_in_component_body::allow(|| {
crate::warnings::signal_read_and_write_in_reactive_scope::allow(|| {
self.inner.set(value);
});
});
}
#[doc(hidden)]
+43 -26
View File
@@ -603,39 +603,56 @@ struct SignalSubscriberDrop<T: 'static, S: Storage<SignalData<T>>> {
origin: &'static std::panic::Location<'static>,
}
impl<T: 'static, S: Storage<SignalData<T>>> Drop for SignalSubscriberDrop<T, S> {
fn drop(&mut self) {
#[cfg(debug_assertions)]
{
tracing::trace!(
"Write on signal at {} finished, updating subscribers",
self.origin
);
pub mod warnings {
//! Warnings that can be triggered by suspicious usage of signals
// Check if the write happened during a render. If it did, we should warn the user that this is generally a bad practice.
if dioxus_core::vdom_is_rendering() {
tracing::warn!(
"Write on signal at {} happened while a component was running. Writing to signals during a render can cause infinite rerenders when you read the same signal in the component. Consider writing to the signal in an effect, future, or event handler if possible.",
self.origin
);
}
use super::*;
use ::warnings::warning;
// Check if the write happened during a scope that the signal is also subscribed to. If it did, this will probably cause an infinite loop.
if let Some(reactive_context) = ReactiveContext::current() {
if let Ok(inner) = self.signal.inner.try_read() {
if let Ok(subscribers) = inner.subscribers.lock() {
for subscriber in subscribers.iter() {
if reactive_context == *subscriber {
let origin = self.origin;
tracing::warn!(
"Write on signal at {origin} finished in {reactive_context} which is also subscribed to the signal. This will likely cause an infinite loop. When the write finishes, {reactive_context} will rerun which may cause the write to be rerun again.\nHINT:\n{SIGNAL_READ_WRITE_SAME_SCOPE_HELP}",
);
}
/// Check if the write happened during a render. If it did, warn the user that this is generally a bad practice.
#[warning]
pub fn signal_write_in_component_body(origin: &'static std::panic::Location<'static>) {
// Check if the write happened during a render. If it did, we should warn the user that this is generally a bad practice.
if dioxus_core::vdom_is_rendering() {
tracing::warn!(
"Write on signal at {} happened while a component was running. Writing to signals during a render can cause infinite rerenders when you read the same signal in the component. Consider writing to the signal in an effect, future, or event handler if possible.",
origin
);
}
}
/// Check if the write happened during a scope that the signal is also subscribed to. If it did, trigger a warning because it will likely cause an infinite loop.
#[warning]
pub fn signal_read_and_write_in_reactive_scope<T: 'static, S: Storage<SignalData<T>>>(
origin: &'static std::panic::Location<'static>,
signal: Signal<T, S>,
) {
// Check if the write happened during a scope that the signal is also subscribed to. If it did, this will probably cause an infinite loop.
if let Some(reactive_context) = ReactiveContext::current() {
if let Ok(inner) = signal.inner.try_read() {
if let Ok(subscribers) = inner.subscribers.lock() {
for subscriber in subscribers.iter() {
if reactive_context == *subscriber {
tracing::warn!(
"Write on signal at {origin} finished in {reactive_context} which is also subscribed to the signal. This will likely cause an infinite loop. When the write finishes, {reactive_context} will rerun which may cause the write to be rerun again.\nHINT:\n{SIGNAL_READ_WRITE_SAME_SCOPE_HELP}",
);
}
}
}
}
}
}
}
#[allow(clippy::no_effect)]
impl<T: 'static, S: Storage<SignalData<T>>> Drop for SignalSubscriberDrop<T, S> {
fn drop(&mut self) {
tracing::trace!(
"Write on signal at {} finished, updating subscribers",
self.origin
);
warnings::signal_write_in_component_body(self.origin);
warnings::signal_read_and_write_in_reactive_scope::<T, S>(self.origin, self.signal);
self.signal.update_subscribers();
}
}