1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
use super::{use_ref, Deps};
use crate::react_bindings;
use wasm_bindgen::{prelude::Closure, JsValue, UnwrapThrowExt};
fn use_effect_inner<G, D>(
effect: impl FnOnce() -> G + 'static,
deps: Deps<D>,
f: impl FnOnce(&JsValue, u8),
) where
G: FnOnce() + 'static,
D: PartialEq + 'static,
{
let create_effect_closure = move || {
Closure::once(move || {
let destructor = effect();
// The effect destructor will definitely be called exactly once by React
Closure::once_into_js(destructor)
})
};
let mut ref_container =
use_ref(None::<(Closure<dyn FnMut() -> JsValue>, Deps<D>, u8)>);
let new_value = match ref_container.current_mut().take() {
Some((old_effect, old_deps, counter)) => {
if deps.is_all() || old_deps != deps {
Some((create_effect_closure(), deps, counter.wrapping_add(1)))
} else {
// Dependencies didn't change
Some((old_effect, old_deps, counter))
}
}
None => Some((create_effect_closure(), deps, 0)),
};
ref_container.set_current(new_value);
let value = ref_container.current();
let (effect, _, counter) =
value.as_ref().expect_throw("no effect data available");
f(effect.as_ref(), *counter);
}
/// Runs a function which contains imperative code that may cause side-effects.
///
/// The given function will run after render is committed to the screen when
/// the given dependencies have changed from last render. The function can
/// return a clean-up function.
///
/// # Example
///
/// ```
/// # use wasm_react::{*, hooks::*};
/// #
/// # fn fetch(url: &str) -> String { String::new() }
/// # struct C { url: &'static str }
/// # impl C {
/// # fn f(&self) {
/// let state = use_state(|| None);
///
/// use_effect({
/// let mut state = state.clone();
/// let url = self.url;
///
/// move || {
/// state.set(|_| Some(fetch(url)));
/// || ()
/// }
/// }, Deps::some(self.url));
/// #
/// # }
/// # }
/// ```
pub fn use_effect<G, D>(effect: impl FnOnce() -> G + 'static, deps: Deps<D>)
where
G: FnOnce() + 'static,
D: PartialEq + 'static,
{
use_effect_inner(effect, deps, react_bindings::use_rust_effect);
}
/// Same as [`use_effect()`], but it fires synchronously after all DOM mutations.
///
/// See [React documentation](https://reactjs.org/docs/hooks-reference.html#uselayouteffect).
pub fn use_layout_effect<G, D>(
effect: impl FnOnce() -> G + 'static,
deps: Deps<D>,
) where
G: FnOnce() + 'static,
D: PartialEq + 'static,
{
use_effect_inner(effect, deps, react_bindings::use_rust_layout_effect);
}