use super::owner::register_effect;
use super::runtime::{EffectId, run_effect_by_id, with_runtime};
pub fn create_effect<F>(f: F)
where
F: FnMut() + 'static,
{
create_effect_id(f);
}
pub(crate) fn create_effect_id<F>(f: F) -> EffectId
where
F: FnMut() + 'static,
{
let id = with_runtime(|rt| rt.allocate_effect(Box::new(f)));
run_effect_by_id(id);
register_effect(id);
id
}
#[cfg(test)]
mod tests {
use super::super::signal::create_signal;
use super::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
#[test]
fn an_unowned_effect_keeps_running() {
let signal = create_signal(0);
let ran = Arc::new(AtomicBool::new(false));
let ran_clone = ran.clone();
create_effect(move || {
let _ = signal.get();
ran_clone.store(true, Ordering::SeqCst);
});
assert!(ran.load(Ordering::SeqCst), "it runs once on creation");
ran.store(false, Ordering::SeqCst);
signal.set(1);
assert!(ran.load(Ordering::SeqCst));
}
#[test]
fn test_write_inside_effect_triggers_other_effects() {
use std::cell::Cell;
use std::rc::Rc;
let source = create_signal(0);
let intermediate = create_signal(0);
let observed = Rc::new(Cell::new(-1));
create_effect(move || {
intermediate.set(source.get() + 1);
});
let observed_b = observed.clone();
create_effect(move || {
observed_b.set(intermediate.get());
});
assert_eq!(observed.get(), 1, "initial chain should have run");
source.set(10);
assert_eq!(
observed.get(),
11,
"write inside effect A must re-run effect B"
);
}
#[test]
fn test_signal_created_inside_effect_is_reactive() {
use std::cell::{Cell, RefCell};
use std::rc::Rc;
let trigger = create_signal(0);
let created: Rc<RefCell<Option<crate::reactive::RwSignal<i32>>>> =
Rc::new(RefCell::new(None));
let created_in_effect = created.clone();
create_effect(move || {
let t = trigger.get();
if created_in_effect.borrow().is_none() {
*created_in_effect.borrow_mut() = Some(create_signal(t));
}
});
trigger.set(1);
trigger.set(2);
let inner = created.borrow().expect("signal created inside effect");
let observed = Rc::new(Cell::new(-1));
let observed_c = observed.clone();
create_effect(move || {
observed_c.set(inner.get());
});
inner.set(42);
assert_eq!(
observed.get(),
42,
"signal created inside an effect must notify its subscribers"
);
}
}