use crate::runtime::{ScopeId, RUNTIME};
pub fn create_effect<F>(f: F)
where
F: Fn() + 'static,
{
let effect_id = RUNTIME.with(|rt| rt.borrow_mut().register_effect(f));
run_effect(effect_id);
}
pub fn on_cleanup(f: impl FnOnce() + 'static) {
RUNTIME.with(|rt| {
rt.borrow_mut().add_cleanup(f);
});
}
pub fn create_scope() -> ScopeId {
RUNTIME.with(|rt| rt.borrow_mut().create_scope())
}
pub fn dispose_scope(scope_id: ScopeId) {
RUNTIME.with(|rt| rt.borrow_mut().dispose_scope(scope_id));
}
pub(crate) fn run_effect(id: usize) {
RUNTIME.with(|rt| {
if rt.borrow().is_effect_disposed(id) {
return;
}
rt.borrow_mut().run_cleanups(id);
let prev = rt.borrow_mut().set_current_effect(Some(id));
let effect_fn = rt.borrow().clone_effect(id);
if let Some(f) = effect_fn {
f();
}
rt.borrow_mut().set_current_effect(prev);
});
}
pub(crate) fn flush_effects() {
loop {
let effect_id = RUNTIME.with(|rt| rt.borrow_mut().pop_pending_effect());
match effect_id {
Some(id) => run_effect(id),
None => break,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::signal::create_signal;
use std::cell::RefCell;
use std::rc::Rc;
#[test]
fn test_effect_runs_immediately() {
let ran = Rc::new(RefCell::new(false));
let ran_clone = ran.clone();
create_effect(move || {
*ran_clone.borrow_mut() = true;
});
assert!(*ran.borrow());
}
#[test]
fn test_effect_auto_run() {
let (count, set_count) = create_signal(0);
let effect_ran = Rc::new(RefCell::new(0));
let effect_ran_clone = effect_ran.clone();
create_effect(move || {
let _ = count.get();
*effect_ran_clone.borrow_mut() += 1;
});
assert_eq!(*effect_ran.borrow(), 1);
set_count.set(1);
assert_eq!(*effect_ran.borrow(), 2);
}
#[test]
fn test_effect_only_runs_when_tracked_signal_changes() {
let (count, set_count) = create_signal(0);
let (other, _set_other) = create_signal(100);
let effect_ran = Rc::new(RefCell::new(0));
let effect_ran_clone = effect_ran.clone();
create_effect(move || {
let _ = count.get();
let _ = other.get_untracked();
*effect_ran_clone.borrow_mut() += 1;
});
assert_eq!(*effect_ran.borrow(), 1);
set_count.set(1);
assert_eq!(*effect_ran.borrow(), 2);
set_count.set(2);
assert_eq!(*effect_ran.borrow(), 3);
}
#[test]
fn test_effect_disposal() {
let (count, set_count) = create_signal(0);
let effect_ran = Rc::new(RefCell::new(0));
let effect_ran_clone = effect_ran.clone();
let scope = create_scope();
create_effect(move || {
let _ = count.get();
*effect_ran_clone.borrow_mut() += 1;
});
assert_eq!(*effect_ran.borrow(), 1);
set_count.set(1);
assert_eq!(*effect_ran.borrow(), 2);
dispose_scope(scope);
set_count.set(2);
assert_eq!(*effect_ran.borrow(), 2); }
#[test]
fn test_nested_scope_disposal() {
let (count, set_count) = create_signal(0);
let inner_ran = Rc::new(RefCell::new(0));
let inner_ran_clone = inner_ran.clone();
let outer = create_scope();
let _inner = create_scope();
create_effect(move || {
let _ = count.get();
*inner_ran_clone.borrow_mut() += 1;
});
assert_eq!(*inner_ran.borrow(), 1);
set_count.set(1);
assert_eq!(*inner_ran.borrow(), 2);
dispose_scope(outer);
set_count.set(2);
assert_eq!(*inner_ran.borrow(), 2); }
#[test]
fn test_on_cleanup_called_on_dispose() {
let cleanup_called = Rc::new(RefCell::new(false));
let cleanup_clone = cleanup_called.clone();
let scope = create_scope();
create_effect(move || {
let cc = cleanup_clone.clone();
on_cleanup(move || {
*cc.borrow_mut() = true;
});
});
assert!(!*cleanup_called.borrow());
dispose_scope(scope);
assert!(*cleanup_called.borrow());
}
#[test]
fn test_on_cleanup_called_on_rerun() {
let (count, set_count) = create_signal(0);
let cleanup_count = Rc::new(RefCell::new(0));
let cleanup_clone = cleanup_count.clone();
create_effect(move || {
let _ = count.get();
let cc = cleanup_clone.clone();
on_cleanup(move || {
*cc.borrow_mut() += 1;
});
});
assert_eq!(*cleanup_count.borrow(), 0);
set_count.set(1); assert_eq!(*cleanup_count.borrow(), 1);
set_count.set(2);
assert_eq!(*cleanup_count.borrow(), 2);
}
#[test]
fn test_multiple_effects() {
let (count, set_count) = create_signal(0);
let effect1_ran = Rc::new(RefCell::new(0));
let effect2_ran = Rc::new(RefCell::new(0));
let e1 = effect1_ran.clone();
let c1 = count.clone();
create_effect(move || {
let _ = c1.get();
*e1.borrow_mut() += 1;
});
let e2 = effect2_ran.clone();
create_effect(move || {
let _ = count.get();
*e2.borrow_mut() += 1;
});
assert_eq!(*effect1_ran.borrow(), 1);
assert_eq!(*effect2_ran.borrow(), 1);
set_count.set(1);
assert_eq!(*effect1_ran.borrow(), 2);
assert_eq!(*effect2_ran.borrow(), 2);
}
}