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}