distinguish between dirty and check in effects, so that memos and signals both work correctly

This commit is contained in:
Greg Johnston 2024-05-20 08:09:04 -04:00
parent 8385287123
commit 1a739015e1
3 changed files with 37 additions and 31 deletions

View file

@ -35,6 +35,7 @@ fn effect_base() -> (Receiver, Owner, Arc<RwLock<EffectInner>>) {
let owner = Owner::new();
let inner = Arc::new(RwLock::new(EffectInner {
dirty: true,
observer,
sources: SourceSet::new(),
}));

View file

@ -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<RwLock<EffectInner>> {
impl ReactiveNode for RwLock<EffectInner> {
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<EffectInner> {
}
fn mark_dirty(&self) {
self.write().or_poisoned().observer.notify()
let mut lock = self.write().or_poisoned();
lock.dirty = true;
lock.observer.notify()
}
}

View file

@ -47,39 +47,37 @@ where
let value = Arc::new(RwLock::new(None::<T>));
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 }
}