Skip to main content

euv_core/reactive/hook/
fn.rs

1use crate::*;
2
3/// Returns the currently active `HookContext`.
4///
5/// If no hook context has been set, creates and stores a default one
6/// in the global `CURRENT_HOOK_CONTEXT` cell so subsequent calls
7/// return the same instance.
8///
9/// # Returns
10///
11/// - `HookContext`: The currently active hook context.
12pub fn get_current_hook_context() -> HookContext {
13    let opt: Option<HookContextRc> = current_hook_context().clone();
14    match opt {
15        Some(rc) => {
16            let mut ctx: HookContext =
17                HookContext::new(Rc::new(RefCell::new(HookContextInner::default())));
18            ctx.set_inner(rc);
19            ctx
20        }
21        None => {
22            let default_rc: HookContextRc = Rc::new(RefCell::new(HookContextInner::default()));
23            *current_hook_context_mut() = Some(default_rc.clone());
24            let mut ctx: HookContext =
25                HookContext::new(Rc::new(RefCell::new(HookContextInner::default())));
26            ctx.set_inner(default_rc);
27            ctx
28        }
29    }
30}
31
32/// Runs a closure with the given `HookContext` set as the active context.
33///
34/// Saves the previous context, sets the new one, executes the closure,
35/// and restores the previous context afterward.
36///
37/// # Arguments
38///
39/// - `HookContext`: The hook context to set as active during closure execution.
40/// - `F`: The closure to execute with the given context.
41///
42/// # Returns
43///
44/// - `R`: The result of the closure execution.
45pub fn with_hook_context<F, R>(context: HookContext, f: F) -> R
46where
47    F: FnOnce() -> R,
48{
49    let previous: Option<HookContextRc> = current_hook_context_mut().take();
50    *current_hook_context_mut() = Some(context.get_inner().clone());
51    let result: R = f();
52    *current_hook_context_mut() = previous;
53    result
54}
55
56/// Creates a new `HookContext` with a fresh inner state.
57///
58/// Delegates to `HookContext::default()` which initializes an empty
59/// hook storage list, a zero hook index, and an empty cleanup list.
60///
61/// # Returns
62///
63/// - `HookContext`: A newly created hook context with default state.
64pub fn create_hook_context() -> HookContext {
65    HookContext::default()
66}
67
68/// Creates a new reactive signal with the given initial value.
69///
70/// Uses the current `HookContext` to maintain signal identity across
71/// re-renders. On the first call at a given hook index, the signal
72/// is created with `init()` and stored. On subsequent re-renders,
73/// the existing signal at that index is returned unchanged.
74///
75/// # Arguments
76///
77/// - `F`: A closure that computes the initial value of the signal.
78///
79/// # Returns
80///
81/// - `Signal<T>`: A reactive signal containing the initialized or existing value.
82pub fn use_signal<T, F>(init: F) -> Signal<T>
83where
84    T: Clone + PartialEq + 'static,
85    F: FnOnce() -> T,
86{
87    let ctx: HookContext = get_current_hook_context();
88    let mut inner: std::cell::RefMut<HookContextInner> = ctx.get_inner().borrow_mut();
89    let index: usize = inner.get_hook_index();
90    inner.set_hook_index(index + 1);
91    if index < inner.get_hooks().len()
92        && let Some(existing) = inner.get_hooks()[index].downcast_ref::<Signal<T>>()
93    {
94        return *existing;
95    }
96    let signal: Signal<T> = Signal::create(init());
97    let cleanup_signal: Signal<T> = signal;
98    inner
99        .get_mut_cleanups()
100        .push(Box::new(move || cleanup_signal.clear_listeners()));
101    if index < inner.get_hooks().len() {
102        inner.get_mut_hooks()[index] = Box::new(signal);
103    } else {
104        inner.get_mut_hooks().push(Box::new(signal));
105    }
106    signal
107}