wasm_react/hooks/
use_effect.rs1use super::{use_ref, Deps};
2use crate::react_bindings;
3use wasm_bindgen::{prelude::Closure, JsValue, UnwrapThrowExt};
4
5pub trait IntoDestructor {
7 #[doc(hidden)]
8 type Destructor: FnOnce() + 'static;
9
10 #[doc(hidden)]
11 fn into_destructor(self) -> Self::Destructor;
12}
13
14impl IntoDestructor for () {
15 type Destructor = fn();
16
17 fn into_destructor(self) -> Self::Destructor {
18 || ()
19 }
20}
21
22impl<F> IntoDestructor for F
23where
24 F: FnOnce() + 'static,
25{
26 type Destructor = F;
27
28 fn into_destructor(self) -> Self::Destructor {
29 self
30 }
31}
32
33fn use_effect_inner<G, D>(
34 effect: impl FnOnce() -> G + 'static,
35 deps: Deps<D>,
36 f: impl FnOnce(&JsValue, u8),
37) where
38 G: IntoDestructor,
39 D: PartialEq + 'static,
40{
41 let create_effect_closure = move || {
42 Closure::once(move || {
43 let destructor = effect();
44
45 Closure::once_into_js(destructor.into_destructor())
47 })
48 };
49
50 let mut ref_container =
51 use_ref(None::<(Closure<dyn FnMut() -> JsValue>, Deps<D>, u8)>);
52
53 let new_value = match ref_container.current_mut().take() {
54 Some((old_effect, old_deps, counter)) => {
55 if deps.is_all() || old_deps != deps {
56 Some((create_effect_closure(), deps, counter.wrapping_add(1)))
57 } else {
58 Some((old_effect, old_deps, counter))
60 }
61 }
62 None => Some((create_effect_closure(), deps, 0)),
63 };
64
65 ref_container.set_current(new_value);
66
67 let value = ref_container.current();
68 let (effect, _, counter) =
69 value.as_ref().expect_throw("no effect data available");
70
71 f(effect.as_ref(), *counter);
72}
73
74pub fn use_effect<G, D>(effect: impl FnOnce() -> G + 'static, deps: Deps<D>)
103where
104 G: IntoDestructor,
105 D: PartialEq + 'static,
106{
107 use_effect_inner(effect, deps, react_bindings::use_rust_effect);
108}
109
110pub fn use_layout_effect<G, D>(
114 effect: impl FnOnce() -> G + 'static,
115 deps: Deps<D>,
116) where
117 G: IntoDestructor,
118 D: PartialEq + 'static,
119{
120 use_effect_inner(effect, deps, react_bindings::use_rust_layout_effect);
121}