diff --git a/reactive_graph/src/effect/effect.rs b/reactive_graph/src/effect/effect.rs index fb64ab677..824ebde02 100644 --- a/reactive_graph/src/effect/effect.rs +++ b/reactive_graph/src/effect/effect.rs @@ -35,6 +35,7 @@ fn effect_base() -> (Receiver, Owner, Arc>) { let owner = Owner::new(); let inner = Arc::new(RwLock::new(EffectInner { + dirty: true, observer, sources: SourceSet::new(), })); diff --git a/reactive_graph/src/effect/inner.rs b/reactive_graph/src/effect/inner.rs index 67c5858c4..3b00af796 100644 --- a/reactive_graph/src/effect/inner.rs +++ b/reactive_graph/src/effect/inner.rs @@ -9,6 +9,7 @@ use or_poisoned::OrPoisoned; use std::sync::{Arc, RwLock, Weak}; pub(crate) struct EffectInner { + pub dirty: bool, pub observer: Sender, pub sources: SourceSet, } @@ -25,14 +26,18 @@ impl ToAnySubscriber for Arc> { impl ReactiveNode for RwLock { fn mark_subscribers_check(&self) {} - // TODO check if this actually works for memos fn update_if_necessary(&self) -> bool { - let sources = { - let guard = self.read().or_poisoned(); - guard.sources.clone() - }; + let mut guard = self.write().or_poisoned(); + let (is_dirty, sources) = + (guard.dirty, (!guard.dirty).then(|| guard.sources.clone())); - for source in sources { + if is_dirty { + guard.dirty = false; + return true; + } + + drop(guard); + for source in sources.into_iter().flatten() { if source.update_if_necessary() { return true; } @@ -45,7 +50,9 @@ impl ReactiveNode for RwLock { } fn mark_dirty(&self) { - self.write().or_poisoned().observer.notify() + let mut lock = self.write().or_poisoned(); + lock.dirty = true; + lock.observer.notify() } } diff --git a/reactive_graph/src/effect/render_effect.rs b/reactive_graph/src/effect/render_effect.rs index 6d943ba78..5583ec653 100644 --- a/reactive_graph/src/effect/render_effect.rs +++ b/reactive_graph/src/effect/render_effect.rs @@ -47,39 +47,37 @@ where let value = Arc::new(RwLock::new(None::)); let owner = Owner::new(); let inner = Arc::new(RwLock::new(EffectInner { + dirty: false, observer, sources: SourceSet::new(), })); - #[cfg(feature = "effects")] - { - let initial_value = Some(owner.with(|| { - inner - .to_any_subscriber() - .with_observer(|| fun(initial_value)) - })); - *value.write().or_poisoned() = initial_value; + let initial_value = Some(owner.with(|| { + inner + .to_any_subscriber() + .with_observer(|| fun(initial_value)) + })); + *value.write().or_poisoned() = initial_value; - Executor::spawn_local({ - let value = Arc::clone(&value); - let subscriber = inner.to_any_subscriber(); + Executor::spawn_local({ + let value = Arc::clone(&value); + let subscriber = inner.to_any_subscriber(); - async move { - while rx.next().await.is_some() { - if subscriber.update_if_necessary() { - subscriber.clear_sources(&subscriber); + async move { + while rx.next().await.is_some() { + if subscriber.update_if_necessary() { + subscriber.clear_sources(&subscriber); - let old_value = - mem::take(&mut *value.write().or_poisoned()); - let new_value = owner.with_cleanup(|| { - subscriber.with_observer(|| fun(old_value)) - }); - *value.write().or_poisoned() = Some(new_value); - } + let old_value = + mem::take(&mut *value.write().or_poisoned()); + let new_value = owner.with(|| { + subscriber.with_observer(|| fun(old_value)) + }); + *value.write().or_poisoned() = Some(new_value); } } - }); - } + } + }); RenderEffect { value, inner } }