repose_core/
state.rs

1use std::any::Any;
2use std::rc::Rc;
3
4use crate::{Signal, on_unmount, reactive, remember_with_key, scoped_effect, signal};
5
6pub struct MutableState<T: Clone + 'static> {
7    inner: Signal<T>,
8    saver: Option<Box<dyn StateSaver<T>>>,
9}
10
11pub trait StateSaver<T>: 'static {
12    fn save(&self, value: &T) -> Box<dyn Any>;
13    fn restore(&self, saved: &dyn Any) -> Option<T>;
14}
15
16pub fn remember_derived<T: Clone + 'static>(
17    key: impl Into<String>,
18    producer: impl Fn() -> T + 'static + Clone,
19) -> std::rc::Rc<crate::Signal<T>> {
20    let key: String = key.into();
21    produce_state(format!("derived:{key}"), producer)
22}
23
24// State holder pattern
25pub trait StateHolder: 'static {
26    type State: Clone;
27    type Event;
28
29    fn initial_state() -> Self::State;
30    fn reduce(state: &Self::State, event: Self::Event) -> Self::State;
31}
32
33/// Lazily produces a Signal<T> (remembered by key) and keeps it up to date
34/// by re-running `producer` under the reactive graph whenever its dependencies change.
35///
36/// - Runs an initial compute immediately to establish dependencies.
37pub fn produce_state<T: Clone + 'static>(
38    key: impl Into<String>,
39    producer: impl Fn() -> T + 'static + Clone,
40) -> Rc<Signal<T>> {
41    let key = key.into();
42    remember_with_key(format!("produce:{key}"), || {
43        let out: Signal<T> = signal(producer());
44        let out_clone = out.clone();
45
46        let obs_id = reactive::new_observer({
47            let producer = producer.clone();
48            move || {
49                let v = producer();
50                out_clone.set(v);
51            }
52        });
53
54        // Establish initial deps and value
55        reactive::run_observer_now(obs_id);
56
57        scoped_effect(move || {
58            on_unmount(move || {
59                reactive::remove_observer(obs_id);
60            })
61        });
62
63        out
64    })
65}