1use std::rc::Rc;
2use wasm_bindgen::{prelude::Closure, UnwrapThrowExt};
3use wasm_bindgen::{JsCast, JsValue};
4
5use wasm_bindgen::prelude::wasm_bindgen;
6
7use super::{IntoOptionalRc, IntoRc};
8
9thread_local! {
10 static CLOSURE_FREE_WITH_USIZE: Closure<dyn FnMut(usize)> =
11 Closure::wrap(Box::new(|n| { unsafe { forgotten::try_free_with_usize(n) }; }) as Box<dyn FnMut(usize)>);
12}
13
14#[wasm_bindgen]
15extern "C" {
16 #[derive(Debug, Clone, PartialEq)]
17 type MutableRefUsizeSlice;
18 #[wasm_bindgen(structural, method, getter)]
19 fn current(this: &MutableRefUsizeSlice) -> Box<[usize]>;
20 #[wasm_bindgen(structural, method, setter)]
21 fn set_current(this: &MutableRefUsizeSlice, val: Box<[usize]>);
22
23 type MySetter;
24 #[wasm_bindgen(structural, method, js_name = "set_state")]
25 fn set_state_with(this: &MySetter, get_value_from_old: &Closure<dyn Fn(usize) -> usize>);
26}
27
28impl MutableRefUsizeSlice {
29 #[inline]
30 fn current_as_vec(&self) -> Vec<usize> {
31 self.current().into_vec()
32 }
33 #[inline]
34 fn set_current_vec(&self, vec: Vec<usize>) {
35 self.set_current(vec.into_boxed_slice())
36 }
37 #[inline]
38 fn push_into_current(&self, state: usize) {
39 let mut arr = self.current_as_vec();
40 arr.push(state);
41 self.set_current_vec(arr);
42 }
43}
44
45#[derive(Debug)]
46pub struct StateSetter<T: ?Sized> {
47 js_setter: react_sys::UseStateUsizeObjectSetter,
48 ref_state_updaters: MutableRefUsizeSlice,
49 _data: std::marker::PhantomData<T>,
50}
51
52impl<T: ?Sized> PartialEq for StateSetter<T> {
53 fn eq(&self, other: &Self) -> bool {
54 self.ref_state_updaters == other.ref_state_updaters
55 }
56}
57
58impl<T: ?Sized> Eq for StateSetter<T> {}
59
60impl<T: ?Sized> Clone for StateSetter<T> {
61 fn clone(&self) -> Self {
62 Self {
63 js_setter: self.js_setter.clone(),
64 ref_state_updaters: self.ref_state_updaters.clone(),
65 _data: std::marker::PhantomData,
66 }
67 }
68}
69
70impl<T: 'static + ?Sized> StateSetter<T> {
71 fn new(
72 js_setter: react_sys::UseStateUsizeObjectSetter,
73 ref_state_updaters: MutableRefUsizeSlice,
74 ) -> Self {
75 Self {
76 js_setter,
77 ref_state_updaters,
78 _data: std::marker::PhantomData,
79 }
80 }
81
82 pub fn set<V: IntoRc<T>>(&self, v: V) {
83 let v: Rc<T> = v.into_rc();
84 let k = forgotten::forget(v).into_shared();
85 let k = k.as_usize();
86 self.js_setter.set_state(*k)
87 }
88
89 pub fn set_from_old<R: IntoRc<T>, F: 'static + Fn(&Rc<T>) -> R>(&self, dispatch: F) {
90 self.set_optional_from_old(move |old| Some((&dispatch)(old).into_rc()))
91 }
92
93 pub fn set_optional_from_old<R: IntoOptionalRc<T>, F: 'static + Fn(&Rc<T>) -> R>(
94 &self,
95 dispatch: F,
96 ) {
97 let ref_state_updaters = self.ref_state_updaters.clone();
98 let closure = move |old_key| {
99 let old = unsafe { forgotten::try_get_with_usize::<Rc<T>>(&old_key) };
100 let old = old.unwrap_throw();
101
102 let new_state: Option<Rc<T>> = dispatch(&old).into_optional_rc();
103
104 if let Some(new_state) = new_state {
105 let new_k = forgotten::forget(new_state).into_shared();
106 let new_k = *new_k.as_usize();
107 ref_state_updaters.push_into_current(new_k);
108 new_k
109 } else {
110 old_key
111 }
112 };
113
114 let closure = Closure::wrap(Box::new(closure) as Box<dyn Fn(usize) -> usize>);
115
116 let (k, closure) = forgotten::forget_and_get(closure);
117 let k = k.into_shared();
118 let k = k.as_usize();
119
120 self.ref_state_updaters.push_into_current(*k);
121
122 let setter: &MySetter = self.js_setter.unchecked_ref();
123 setter.set_state_with(closure.as_ref());
124 }
125}
126
127pub fn use_state_value<T: 'static + ?Sized>(initial_value: Rc<T>) -> (Rc<T>, StateSetter<T>) {
128 use_state(move || Rc::clone(&initial_value))
129}
130
131pub fn use_state<T: 'static + ?Sized, F: Fn() -> Rc<T>>(
132 get_initial_value: F,
133) -> (Rc<T>, StateSetter<T>) {
134 let ref_all_persisted = use_ref_all_persisted();
135
136 let ret = react_sys::use_state_usize_with(&mut || {
137 let state = get_initial_value();
138 let k = forgotten::forget(state);
139 let k = k.into_shared();
140 let k = *k.as_usize();
141 let mut arr = ref_all_persisted.current().into_vec();
142 arr.push(k);
143 ref_all_persisted.set_current(arr.into_boxed_slice());
144 k
145 });
146
147 let state = ret.value();
148
149 use_clean_outdated_persisted(state, ref_all_persisted.clone());
150
151 let state = unsafe { forgotten::try_get_with_usize::<Rc<T>>(&state) };
152 let state = state.unwrap_throw();
153 let state = Rc::clone(state.as_ref());
154
155 let setter = ret.setter();
156
157 (state, StateSetter::new(setter, ref_all_persisted))
158}
159
160fn use_ref_all_persisted() -> MutableRefUsizeSlice {
161 let ref_all_persisted = react_sys::use_ref(&JsValue::UNDEFINED);
162 let not_initialized = ref_all_persisted.current().is_falsy();
163 let ref_all_persisted: MutableRefUsizeSlice = ref_all_persisted.unchecked_into();
164 if not_initialized {
165 ref_all_persisted.set_current_vec(Vec::<usize>::with_capacity(4));
166 }
167 ref_all_persisted
168}
169
170fn use_clean_outdated_persisted(state: usize, ref_all_persisted: MutableRefUsizeSlice) {
171 crate::use_effect!(
172 (state, ref_all_persisted) => {
173 let state = state.as_ref();
174 let state_updaters = ref_all_persisted.current();
175 if state_updaters.len()>0 {
176 for k in state_updaters.iter() {
177 if k == state {
178 continue;
179 }
180 #[cfg(debug_assertions)]
181 {
182 let cleaned = unsafe { forgotten::try_free_with_usize(*k) };
183 if !cleaned {
184 web_sys::console::error_2(
185 &"use_state*: forgotten key invalid or already dropped. forgotten::Key = "
186 .into(),
187 &(*k).into(),
188 )
189 }
190 }
191
192 #[cfg(not(debug_assertions))]
193 unsafe {
194 forgotten::try_free_with_usize(k)
195 };
196 }
197 let mut state_updaters = Vec::with_capacity(4);
198 state_updaters.push(*state);
199 ref_all_persisted.set_current_vec(state_updaters);
200 }
201 }
202 );
203}
204
205#[macro_export]
206macro_rules! use_state {
207 ($e:expr) => {
208 $crate::use_state_value($crate::auto_wrap_rc!($e))
209 };
210 (() => $e:expr) => {
211 $crate::use_state(move || $crate::auto_wrap_rc!($e))
212 };
213}