Skip to main content

rue_core/reactive/
computed.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3use crate::reactive::context;
4
5/// A computed value (like Vue 3's `computed()`).
6/// Lazily evaluates a closure, caching the result and re-evaluating
7/// when its dependencies change.
8pub struct Computed<T: Clone> {
9    value: Rc<RefCell<T>>,
10    dirty: Rc<RefCell<bool>>,
11    compute_fn: Rc<dyn Fn() -> T>,
12    effect_id: usize,
13}
14
15impl<T: Clone + 'static> Computed<T> {
16    /// Create a new computed value from the given computation function.
17    pub fn new<F: Fn() -> T + 'static>(f: F) -> Self {
18        let compute_fn: Rc<dyn Fn() -> T> = Rc::new(f);
19        let initial = (compute_fn)();
20        let value = Rc::new(RefCell::new(initial));
21        let dirty = Rc::new(RefCell::new(true));
22
23        let value_clone = value.clone();
24        let dirty_clone = dirty.clone();
25        let compute_clone = compute_fn.clone();
26
27        let effect_id = context::run_effect(Box::new(move || {
28            // Re-compute
29            let result = (compute_clone)();
30            *value_clone.borrow_mut() = result;
31            *dirty_clone.borrow_mut() = false;
32        }));
33
34        // Run the effect once to establish dependencies
35        let compute_for_effect = compute_fn.clone();
36        context::track_effect(effect_id, move || {
37            let _ = (compute_for_effect)();
38        });
39
40        Computed {
41            value,
42            dirty,
43            compute_fn,
44            effect_id,
45        }
46    }
47
48    /// Get the computed value. This will re-compute if dependencies have changed.
49    pub fn get(&self) -> std::cell::Ref<'_, T> {
50        context::track_signal(self.effect_id);
51        if *self.dirty.borrow() {
52            let result = (self.compute_fn)();
53            *self.value.borrow_mut() = result;
54            *self.dirty.borrow_mut() = false;
55        }
56        self.value.borrow()
57    }
58}
59
60/// Create a new computed value (convenience function).
61pub fn computed<T: Clone + 'static, F: Fn() -> T + 'static>(f: F) -> Computed<T> {
62    Computed::new(f)
63}