dioxus_use_computed/hooks/
use_computed.rs

1use std::{cell::RefCell, rc::Rc};
2
3use dioxus_lib::prelude::{use_hook, use_signal, ReadOnlySignal, Readable, Writable};
4mod warnings {
5    pub use warnings::Warning;
6}
7pub use warnings::Warning;
8
9
10/// Alternative to [use_memo](dioxus_lib::prelude::use_memo)
11/// Benefits:
12/// - No unnecessary rerenders
13/// Downsides:
14/// - T needs to be Clone (cannot be avoided)
15pub fn use_computed<T: 'static + Clone, D: PartialEq + 'static>(
16    deps: D,
17    init: impl FnOnce() -> T,
18) -> T {
19    use_computed_with_prev(deps, |_| init())
20}
21
22/// Alternative to [use_memo](dioxus_lib::prelude::use_memo)
23/// Benefits:
24/// - No unnecessary rerenders
25/// - Prev value is `T` instead of `&mut T`
26/// Downsides:
27/// - T needs to be Clone (cannot be avoided)
28pub fn use_computed_with_prev<T: 'static + Clone, D: PartialEq + 'static>(
29    deps: D,
30    init: impl FnOnce(Option<T>) -> T,
31) -> T {
32    struct Memoized<T, D> {
33        value: T,
34        deps: D,
35    }
36    let memo_signal = use_hook(|| Rc::new(RefCell::new(None::<Memoized<T, D>>)));
37    let mut memo = memo_signal.borrow_mut();
38
39    let deps_have_changed = memo.as_ref().map(|memo| &memo.deps) != Some(&deps);
40
41    let new_value = if deps_have_changed {
42        let prev_value = memo.take().map(|memo| memo.value);
43        Some(init(prev_value))
44    } else {
45        None
46    };
47
48    if let Some(new_value) = new_value {
49        let new_memoized_value = Memoized {
50            value: new_value,
51            deps,
52        };
53        *memo = Some(new_memoized_value);
54    }
55
56    memo.as_ref().unwrap().value.clone()
57}
58
59/// Alternative to [use_memo](dioxus_lib::prelude::use_memo)
60/// Benefits:
61/// - No unnecessary rerenders
62/// Downsides:
63/// - D needs to be Clone (cannot be avoided)
64pub fn use_computed_signal<T: 'static, D: PartialEq + Clone + 'static>(
65    deps: D,
66    init: impl Fn() -> T,
67) -> ReadOnlySignal<T> {
68    use_computed_signal_with_prev(deps, move |_| init())
69}
70
71/// Alternative to [use_memo](dioxus_lib::prelude::use_memo)
72/// Benefits:
73/// - No unnecessary rerenders
74/// - Access the previous computed value
75/// Downsides:
76/// - D needs to be Clone (cannot be avoided)
77/// - Prev value is `&mut T` instead of `T`
78pub fn use_computed_signal_with_prev<T: 'static, D: PartialEq + Clone + 'static>(
79    deps: D,
80    init: impl Fn(Option<&mut T>) -> T,
81) -> ReadOnlySignal<T> {
82    let mut deps_signal = use_signal::<D>(|| deps.clone());
83    let mut value_signal = use_signal::<T>(|| init(None));
84
85    let deps_have_changed = *deps_signal.peek() != deps;
86
87    if deps_have_changed {
88        dioxus_lib::prelude::warnings::signal_write_in_component_body::allow(|| {
89            let mut memoized_deps = deps_signal.write();
90            let mut memoized_value = value_signal.write();
91
92            let new_value = init(Some(&mut *memoized_value));
93
94            *memoized_value = new_value;
95            *memoized_deps = deps;
96        });
97    }
98
99    value_signal.into()
100}