repose_core/
state.rs

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