repose_core/
effects_ext.rs

1use crate::{Dispose, on_unmount, remember, scoped_effect};
2use std::cell::RefCell;
3
4/// cleanup on key change or unmount
5pub fn disposable_effect<K: PartialEq + Clone + 'static>(
6    key: K,
7    effect: impl FnOnce() -> Dispose + 'static,
8) {
9    // Slot-based (like Compose). For branch-stability use `remember_with_key` variants later.
10    let last_key = remember(|| RefCell::new(None::<K>));
11    let cleanup_slot = remember(|| RefCell::new(None::<Dispose>));
12    let installed = remember(|| RefCell::new(false));
13
14    // Install a single unmount disposer for this callsite.
15    if !*installed.borrow() {
16        *installed.borrow_mut() = true;
17        let cleanup_slot = cleanup_slot.clone();
18        scoped_effect(move || {
19            on_unmount(move || {
20                if let Some(d) = cleanup_slot.borrow_mut().take() {
21                    d.run();
22                }
23            })
24        });
25    }
26
27    // Key change: cleanup previous + run new effect
28    let changed = last_key.borrow().as_ref() != Some(&key);
29    if changed {
30        *last_key.borrow_mut() = Some(key);
31
32        if let Some(d) = cleanup_slot.borrow_mut().take() {
33            d.run();
34        }
35
36        let d = effect();
37        *cleanup_slot.borrow_mut() = Some(d);
38    }
39}
40
41/// runs on every recomposition
42pub fn side_effect(effect: impl Fn()) {
43    effect();
44}
45
46/// Internal implementation: keyed by a per-callsite id string.
47pub fn launched_effect_internal<K: PartialEq + Clone + 'static>(
48    callsite: &'static str,
49    key: K,
50    effect: impl FnOnce() + 'static,
51) {
52    // One slot per call-site, with K baked into its type.
53    let last_key =
54        crate::remember_with_key(format!("launched:{callsite}"), || RefCell::new(None::<K>));
55
56    let mut last = last_key.borrow_mut();
57    if last.as_ref() != Some(&key) {
58        *last = Some(key);
59        // doesn't cancel on unmount
60        effect();
61    }
62}
63
64#[macro_export] // Should probably move this to macros (might want to move the above part too?)
65macro_rules! launched_effect {
66    ($key:expr, $effect:expr) => {
67        $crate::effects_ext::launched_effect_internal(
68            concat!(module_path!(), ":", line!(), ":", column!()),
69            $key,
70            $effect,
71        )
72    };
73}