1use std::any::Any;
2use std::cell::{Ref, RefCell, RefMut};
3use std::rc::Rc;
4
5use crate::{Signal, on_unmount, reactive, remember_with_key, request_frame, scoped_effect, signal};
6
7pub struct MutableState<T: Clone + 'static> {
8 inner: Signal<T>,
9 saver: Option<Box<dyn StateSaver<T>>>,
10}pub trait StateSaver<T>: 'static {
11 fn save(&self, value: &T) -> Box<dyn Any>;
12 fn restore(&self, saved: &dyn Any) -> Option<T>;
13}
14
15pub fn remember_derived<T: Clone + 'static>(
16 key: impl Into<String>,
17 producer: impl Fn() -> T + 'static + Clone,
18) -> std::rc::Rc<crate::Signal<T>> {
19 let key: String = key.into();
20 produce_state(format!("derived:{key}"), producer)
21}
22
23pub trait StateHolder: 'static {
25 type State: Clone;
26 type Event;
27
28 fn initial_state() -> Self::State;
29 fn reduce(state: &Self::State, event: Self::Event) -> Self::State;
30}
31
32pub fn produce_state<T: Clone + 'static>(
37 key: impl Into<String>,
38 producer: impl Fn() -> T + 'static + Clone,
39) -> Rc<Signal<T>> {
40 produce_state_inner(key.into(), producer, |out, v| out.set(v))
41}
42
43pub fn produce_state_eq<T: Clone + PartialEq + 'static>(
47 key: impl Into<String>,
48 producer: impl Fn() -> T + 'static + Clone,
49) -> Rc<Signal<T>> {
50 produce_state_inner(key.into(), producer, |out, v| out.set_neq(v))
51}
52
53fn produce_state_inner<T: Clone + 'static>(
54 key: String,
55 producer: impl Fn() -> T + 'static + Clone,
56 write: impl Fn(Signal<T>, T) + 'static + Copy,
57) -> Rc<Signal<T>> {
58 remember_with_key(format!("produce:{key}"), || {
59 let out: Signal<T> = signal(producer());
60 let out_clone = out.clone();
61
62 let obs_id = reactive::new_observer({
63 let producer = producer.clone();
64 move || {
65 let v = producer();
66 write(out_clone.clone(), v);
67 }
68 });
69
70 reactive::run_observer_now(obs_id);
72
73 scoped_effect(move || {
74 on_unmount(move || {
75 reactive::remove_observer(obs_id);
76 })
77 });
78
79 out
80 })
81}
82
83pub struct Mutable<T: 'static>(Rc<RefCell<T>>);
91
92impl<T: 'static> Clone for Mutable<T> {
95 fn clone(&self) -> Self {
96 Self(self.0.clone())
97 }
98}
99
100impl<T: 'static> Mutable<T> {
101 pub fn new(v: T) -> Self {
102 Self(Rc::new(RefCell::new(v)))
103 }
104
105 pub fn get(&self) -> Ref<'_, T> {
106 self.0.borrow()
107 }
108
109 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
111 f(&*self.0.borrow())
112 }
113
114 pub fn set(&self, v: T) {
115 *self.0.borrow_mut() = v;
116 crate::signal_fired();
117 request_frame();
118 }
119
120 pub fn set_neq(&self, v: T)
123 where
124 T: PartialEq,
125 {
126 {
127 let mut b = self.0.borrow_mut();
128 if *b == v {
129 return;
130 }
131 *b = v;
132 }
133 crate::signal_fired();
134 request_frame();
135 }
136
137 pub fn update(&self, f: impl FnOnce(&mut T)) {
138 f(&mut *self.0.borrow_mut());
139 crate::signal_fired();
140 request_frame();
141 }
142
143 pub fn update_neq(&self, f: impl FnOnce(&mut T))
146 where
147 T: PartialEq + Clone,
148 {
149 let changed = {
150 let mut b = self.0.borrow_mut();
151 let before = (*b).clone();
152 f(&mut *b);
153 *b != before
154 };
155 if changed {
156 crate::signal_fired();
157 request_frame();
158 }
159 }
160
161 pub fn borrow_mut_silent(&self) -> RefMut<'_, T> {
163 self.0.borrow_mut()
164 }
165
166 pub fn as_rc(&self) -> Rc<RefCell<T>> {
167 self.0.clone()
168 }
169}
170
171#[track_caller]
173pub fn remember_mutable<T: 'static>(init: impl FnOnce() -> T) -> Mutable<T> {
174 crate::remember(|| Mutable::new(init())).as_ref().clone()
175}
176
177#[track_caller]
179pub fn remember_mutable_with_key<T: 'static>(
180 key: impl Into<String>,
181 init: impl FnOnce() -> T,
182) -> Mutable<T> {
183 remember_with_key(key, || Mutable::new(init())).as_ref().clone()
184}
185
186#[track_caller]
192pub fn remember_reducer<H: StateHolder>() -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
193where
194 H::State: 'static,
195 H::Event: 'static,
196{
197 let state = remember_mutable(|| H::initial_state());
198 let dispatch = {
199 let state = state.clone();
200 move |ev: H::Event| {
201 state.update(|s| *s = H::reduce(s, ev));
202 }
203 };
204 (state, dispatch)
205}
206
207#[track_caller]
209pub fn remember_reducer_with_key<H: StateHolder>(
210 key: impl Into<String>,
211) -> (Mutable<H::State>, impl Fn(H::Event) + Clone)
212where
213 H::State: 'static,
214 H::Event: 'static,
215{
216 let state = remember_mutable_with_key(key, || H::initial_state());
217 let dispatch = {
218 let state = state.clone();
219 move |ev: H::Event| {
220 state.update(|s| *s = H::reduce(s, ev));
221 }
222 };
223 (state, dispatch)
224}