maple_core/reactive.rs
1//! Reactive primitives.
2
3mod effect;
4mod signal;
5mod signal_vec;
6
7pub use effect::*;
8pub use signal::*;
9pub use signal_vec::*;
10
11/// Creates a new reactive root. Generally, you won't need this method as it is called automatically in [`render`](crate::render()).
12///
13/// # Example
14/// ```
15/// use maple_core::prelude::*;
16///
17/// let trigger = Signal::new(());
18/// let counter = Signal::new(0);
19///
20/// let owner = create_root(cloned!((trigger, counter) => move || {
21/// create_effect(move || {
22/// trigger.get(); // subscribe to trigger
23/// counter.set(*counter.get_untracked() + 1);
24/// });
25/// }));
26///
27/// assert_eq!(*counter.get(), 1);
28///
29/// trigger.set(());
30/// assert_eq!(*counter.get(), 2);
31///
32/// drop(owner);
33/// trigger.set(());
34/// assert_eq!(*counter.get(), 2); // should not be updated because owner was dropped
35/// ```
36#[must_use = "create_root returns the owner of the effects created inside this scope"]
37pub fn create_root<'a>(callback: impl FnOnce() + 'a) -> Owner {
38 /// Internal implementation: use dynamic dispatch to reduce code bloat.
39 fn internal<'a>(callback: Box<dyn FnOnce() + 'a>) -> Owner {
40 OWNER.with(|owner| {
41 let outer_owner = owner.replace(Some(Owner::new()));
42 callback();
43
44 owner
45 .replace(outer_owner)
46 .expect("Owner should be valid inside the reactive root")
47 })
48 }
49
50 internal(Box::new(callback))
51}
52
53#[cfg(test)]
54mod tests {
55 use std::cell::RefCell;
56 use std::rc::Rc;
57
58 use super::*;
59
60 #[test]
61 fn drop_owner_inside_effect() {
62 let owner = Rc::new(RefCell::new(None));
63
64 *owner.borrow_mut() = Some(create_root({
65 let owner = Rc::clone(&owner);
66 move || {
67 let owner = owner.take();
68 drop(owner)
69 }
70 }));
71 }
72}