reactive_graph/computed.rs
1//! Computed reactive values that derive from other reactive values.
2
3mod arc_memo;
4mod async_derived;
5mod inner;
6mod memo;
7mod selector;
8use crate::{
9 prelude::*,
10 signal::RwSignal,
11 wrappers::{
12 read::Signal,
13 write::{IntoSignalSetter, SignalSetter},
14 },
15};
16pub use arc_memo::*;
17pub use async_derived::*;
18pub use memo::*;
19pub use selector::*;
20
21/// Derives a reactive slice of an [`RwSignal`].
22///
23/// Slices have the same guarantees as [`Memo`s](crate::computed::Memo):
24/// they only emit their value when it has actually been changed.
25///
26/// Slices need a getter and a setter, and you must make sure that
27/// the setter and getter only touch their respective field and nothing else.
28/// They optimally should not have any side effects.
29///
30/// You can use slices whenever you want to react to only parts
31/// of a bigger signal. The prime example would be state management,
32/// where you want all state variables grouped together, but also need
33/// fine-grained signals for each or some of these variables.
34/// In the example below, setting an auth token will only trigger
35/// the token signal, but none of the other derived signals.
36/// ```
37/// # use reactive_graph::prelude::*; let owner = reactive_graph::owner::Owner::new(); owner.set();
38/// # use reactive_graph::effect::Effect;
39/// # use reactive_graph::signal::RwSignal;
40/// # use reactive_graph::computed::*;
41///
42/// // some global state with independent fields
43/// #[derive(Default, Clone, Debug)]
44/// struct GlobalState {
45/// count: u32,
46/// name: String,
47/// }
48///
49/// let state = RwSignal::new(GlobalState::default());
50///
51/// // `create_slice` lets us create a "lens" into the data
52/// let (count, set_count) = create_slice(
53/// // we take a slice *from* `state`
54/// state,
55/// // our getter returns a "slice" of the data
56/// |state| state.count,
57/// // our setter describes how to mutate that slice, given a new value
58/// |state, n| state.count = n,
59/// );
60///
61/// // this slice is completely independent of the `count` slice
62/// // neither of them will cause the other to rerun
63/// let (name, set_name) = create_slice(
64/// // we take a slice *from* `state`
65/// state,
66/// // our getter returns a "slice" of the data
67/// |state| state.name.clone(),
68/// // our setter describes how to mutate that slice, given a new value
69/// |state, n| state.name = n,
70/// );
71///
72/// # if false { // don't run effects in doctest
73/// Effect::new(move |_| {
74/// println!("name is {}", name.get());
75/// });
76/// Effect::new(move |_| {
77/// println!("count is {}", count.get());
78/// });
79/// # }
80///
81/// // setting count only causes count to log, not name
82/// set_count.set(42);
83///
84/// // setting name only causes name to log, not count
85/// set_name.set("Bob".into());
86/// ```
87#[track_caller]
88pub fn create_slice<T, O, S>(
89 signal: RwSignal<T>,
90 getter: impl Fn(&T) -> O + Copy + Send + Sync + 'static,
91 setter: impl Fn(&mut T, S) + Copy + Send + Sync + 'static,
92) -> (Signal<O>, SignalSetter<S>)
93where
94 T: Send + Sync + 'static,
95 O: PartialEq + Send + Sync + 'static,
96{
97 (
98 create_read_slice(signal, getter),
99 create_write_slice(signal, setter),
100 )
101}
102
103/// Takes a memoized, read-only slice of a signal. This is equivalent to the
104/// read-only half of [`create_slice`].
105#[track_caller]
106pub fn create_read_slice<T, O>(
107 signal: RwSignal<T>,
108 getter: impl Fn(&T) -> O + Copy + Send + Sync + 'static,
109) -> Signal<O>
110where
111 T: Send + Sync + 'static,
112 O: PartialEq + Send + Sync + 'static,
113{
114 Memo::new(move |_| signal.with(getter)).into()
115}
116
117/// Creates a setter to access one slice of a signal. This is equivalent to the
118/// write-only half of [`create_slice`].
119#[track_caller]
120pub fn create_write_slice<T, O>(
121 signal: RwSignal<T>,
122 setter: impl Fn(&mut T, O) + Copy + Send + Sync + 'static,
123) -> SignalSetter<O>
124where
125 T: Send + Sync + 'static,
126{
127 let setter = move |value| signal.update(|x| setter(x, value));
128 setter.into_signal_setter()
129}
130
131/// Creates a new memoized, computed reactive value.
132#[inline(always)]
133#[track_caller]
134#[deprecated = "This function is being removed to conform to Rust idioms. \
135 Please use `Memo::new()` instead."]
136pub fn create_memo<T>(
137 fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static,
138) -> Memo<T>
139where
140 T: PartialEq + Send + Sync + 'static,
141{
142 Memo::new(fun)
143}
144
145/// Creates a new memo by passing a function that computes the value.
146#[inline(always)]
147#[track_caller]
148#[deprecated = "This function is being removed to conform to Rust idioms. \
149 Please use `Memo::new_owning()` instead."]
150pub fn create_owning_memo<T>(
151 fun: impl Fn(Option<T>) -> (T, bool) + Send + Sync + 'static,
152) -> Memo<T>
153where
154 T: PartialEq + Send + Sync + 'static,
155{
156 Memo::new_owning(fun)
157}
158
159/// A conditional signal that only notifies subscribers when a change
160/// in the source signal’s value changes whether the given function is true.
161#[inline(always)]
162#[track_caller]
163#[deprecated = "This function is being removed to conform to Rust idioms. \
164 Please use `Selector::new()` instead."]
165pub fn create_selector<T>(
166 source: impl Fn() -> T + Clone + Send + Sync + 'static,
167) -> Selector<T>
168where
169 T: PartialEq + Eq + Send + Sync + Clone + std::hash::Hash + 'static,
170{
171 Selector::new(source)
172}
173
174/// Creates a conditional signal that only notifies subscribers when a change
175/// in the source signal’s value changes whether the given function is true.
176#[inline(always)]
177#[track_caller]
178#[deprecated = "This function is being removed to conform to Rust idioms. \
179 Please use `Selector::new_with_fn()` instead."]
180pub fn create_selector_with_fn<T>(
181 source: impl Fn() -> T + Clone + Send + Sync + 'static,
182 f: impl Fn(&T, &T) -> bool + Send + Sync + Clone + 'static,
183) -> Selector<T>
184where
185 T: PartialEq + Eq + Send + Sync + Clone + std::hash::Hash + 'static,
186{
187 Selector::new_with_fn(source, f)
188}