Skip to main content

rue_core/reactive/
context.rs

1use std::cell::RefCell;
2
3thread_local! {
4    static CURRENT_EFFECT: RefCell<Option<EffectId>> = const { RefCell::new(None) };
5    static EFFECTS: RefCell<Vec<Box<dyn Fn()>>> = const { RefCell::new(Vec::new()) };
6    static TRACKING: RefCell<Vec<Vec<EffectId>>> = const { RefCell::new(Vec::new()) };
7}
8
9type EffectId = usize;
10
11/// Register an effect to be re-run when its dependencies change.
12pub fn run_effect(f: Box<dyn Fn()>) -> EffectId {
13    EFFECTS.with(|e| {
14        let mut effects = e.borrow_mut();
15        let id = effects.len();
16        effects.push(f);
17        id
18    })
19}
20
21/// Execute a function while tracking which signals it reads.
22pub fn track_effect(effect_id: EffectId, f: impl Fn()) {
23    // Set current effect
24    let prev = CURRENT_EFFECT.with(|c| c.replace(Some(effect_id)));
25    TRACKING.with(|t| {
26        // Ensure we have a tracking list slot for this effect
27        let mut tracking = t.borrow_mut();
28        while tracking.len() <= effect_id {
29            tracking.push(Vec::new());
30        }
31        tracking[effect_id].clear();
32    });
33
34    // Run the function (signals will register themselves via track_signal)
35    f();
36
37    // Reset current effect
38    CURRENT_EFFECT.with(|c| {
39        c.replace(prev);
40    });
41}
42
43/// Called by Signal::get() to register the current effect as a subscriber.
44pub fn track_signal(signal_id: usize) {
45    CURRENT_EFFECT.with(|c| {
46        if let Some(effect_id) = *c.borrow() {
47            TRACKING.with(|t| {
48                let mut tracking = t.borrow_mut();
49                if effect_id < tracking.len() {
50                    if !tracking[effect_id].contains(&signal_id) {
51                        tracking[effect_id].push(signal_id);
52                    }
53                }
54            });
55        }
56    });
57}
58
59/// Get all effect IDs that depend on a given signal.
60pub fn get_dependent_effects(signal_id: usize) -> Vec<EffectId> {
61    TRACKING.with(|t| {
62        let tracking = t.borrow();
63        let mut deps = Vec::new();
64        for (effect_id, signals) in tracking.iter().enumerate() {
65            if signals.contains(&signal_id) {
66                deps.push(effect_id);
67            }
68        }
69        deps
70    })
71}
72
73/// Schedule effects that depend on the given signal to be re-run.
74/// Returns the effect IDs that need re-running (caller can execute them).
75pub fn trigger_effects(signal_id: usize) {
76    let deps = get_dependent_effects(signal_id);
77    EFFECTS.with(|e| {
78        let effects = e.borrow();
79        for dep_id in deps {
80            if dep_id < effects.len() {
81                let effect = &effects[dep_id];
82                track_effect(dep_id, effect);
83            }
84        }
85    });
86}