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