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