1use std::cell::RefCell;
2use std::rc::Rc;
3
4#[derive(Clone)]
5pub struct Dispose(Rc<RefCell<Option<Box<dyn FnOnce()>>>>);
6
7impl Dispose {
8 pub fn new(f: impl FnOnce() + 'static) -> Self {
9 Self(Rc::new(RefCell::new(Some(Box::new(f)))))
10 }
11
12 pub fn run(&self) {
14 if let Some(f) = self.0.borrow_mut().take() {
15 f()
16 }
17 }
18}
19
20pub fn effect<F>(f: F) -> Dispose
25where
26 F: FnOnce() -> Dispose + 'static,
27{
28 let d = f();
29
30 if let Some(scope) = crate::scope::current_scope() {
31 let d2 = d.clone();
32 scope.add_disposer(move || d2.run());
33 } else {
34 debug_assert!(
35 false,
36 "effect called without a current Scope; cleanup cannot be tracked"
37 );
38 log::error!("effect called without a current Scope; cleanup untracked");
39 }
40
41 d
42}
43
44#[track_caller]
47pub fn effect_once(f: impl FnOnce() -> Dispose + 'static) -> Dispose {
48 let loc = std::panic::Location::caller();
49 let key = format!("effect:{}:{}:{}", loc.file(), loc.line(), loc.column());
50 let slot = crate::remember_with_key(key, || RefCell::new(None::<Dispose>));
51 if let Some(d) = slot.borrow().as_ref() {
52 return d.clone();
53 }
54 let d = effect(f);
55 *slot.borrow_mut() = Some(d.clone());
56 d
57}
58pub fn on_unmount(f: impl FnOnce() + 'static) -> Dispose {
60 Dispose::new(f)
61}