repose_core/
effects_ext.rs1use crate::{Dispose, on_unmount, scoped_effect};
2use std::cell::RefCell;
3
4macro_rules! effect_key {
6 ($prefix:literal) => {{
7 let loc = std::panic::Location::caller();
8 format!("{}:{}:{}:{}", $prefix, loc.file(), loc.line(), loc.column())
9 }};
10}
11
12#[track_caller]
15pub fn disposable_effect<K: PartialEq + Clone + 'static>(
16 key: K,
17 effect: impl FnOnce() -> Dispose + 'static,
18) {
19 disposable_effect_with_callsite(effect_key!("de"), key, effect);
20}
21
22pub fn side_effect(effect: impl Fn()) {
24 effect();
25}
26
27pub fn launched_effect_internal<K: PartialEq + Clone + 'static>(
31 callsite: &'static str,
32 key: K,
33 effect: impl FnOnce() -> Dispose + 'static,
34) {
35 disposable_effect_with_callsite(format!("launched:{callsite}"), key, effect);
36}
37
38fn disposable_effect_with_callsite<K: PartialEq + Clone + 'static>(
39 callsite: String,
40 key: K,
41 effect: impl FnOnce() -> Dispose + 'static,
42) {
43 let last_key = crate::remember_with_key(format!("{callsite}:last"), || RefCell::new(None::<K>));
44 let cleanup_key = format!("{callsite}:cleanup");
45 let cleanup_slot =
46 crate::remember_with_key(cleanup_key.clone(), || RefCell::new(None::<Dispose>));
47 let owner =
48 crate::runtime::scope_owner_token(crate::scope_cache::current_scope_key().as_deref());
49 let installed = crate::remember_with_key(format!("{callsite}:installed:{owner}"), || {
50 RefCell::new(false)
51 });
52
53 if !*installed.borrow() {
54 *installed.borrow_mut() = true;
55 let cleanup_slot = cleanup_slot.clone();
56 let cleanup_key = cleanup_key.clone();
57 let cleanup_owner = owner.clone();
58 scoped_effect(move || {
59 on_unmount(move || {
60 let disposer = cleanup_slot.borrow().as_ref().cloned();
61 if let Some(disposer) = disposer.as_ref() {
62 crate::runtime::remove_keyed_disposer_for_owner(
63 &cleanup_key,
64 disposer,
65 &cleanup_owner,
66 );
67 }
68 let (_, removed) =
69 crate::runtime::release_keyed_owner(&cleanup_key, &cleanup_owner);
70 drop(removed);
71 })
72 });
73 }
74
75 let changed = last_key.borrow().as_ref() != Some(&key);
76 if changed {
77 let old_key = {
78 let mut last = last_key.borrow_mut();
79 last.replace(key.clone())
80 };
81 drop(old_key);
82 let previous = {
83 let mut slot = cleanup_slot.borrow_mut();
84 slot.take()
85 };
86 if let Some(previous) = previous {
87 crate::runtime::remove_keyed_disposer_for_owner(&cleanup_key, &previous, &owner);
88 }
89 let d = effect();
90 let old = {
91 let mut slot = cleanup_slot.borrow_mut();
92 slot.replace(d.clone())
93 };
94 drop(old);
95 crate::runtime::register_keyed_disposer_for_owner(cleanup_key.clone(), d, owner.clone());
96 }
97}
98
99pub fn launched_effect_uncancelled_internal<K: PartialEq + Clone + 'static>(
103 callsite: &'static str,
104 key: K,
105 effect: impl FnOnce() + 'static,
106) {
107 let last_key =
108 crate::remember_with_key(format!("launched:{callsite}"), || RefCell::new(None::<K>));
109
110 let changed = last_key.borrow().as_ref() != Some(&key);
111 if changed {
112 let old = {
113 let mut last = last_key.borrow_mut();
114 last.replace(key.clone())
115 };
116 drop(old);
117 effect();
118 }
119}
120
121#[macro_export] macro_rules! launched_effect {
123 ($key:expr, $effect:expr) => {
124 $crate::effects_ext::launched_effect_internal(
125 concat!(module_path!(), ":", line!(), ":", column!()),
126 $key,
127 $effect,
128 )
129 };
130}
131
132#[macro_export]
133macro_rules! launched_effect_uncancelled {
134 ($key:expr, $effect:expr) => {
135 $crate::effects_ext::launched_effect_uncancelled_internal(
136 concat!(module_path!(), ":", line!(), ":", column!()),
137 $key,
138 $effect,
139 )
140 };
141}