diff --git a/packages/hooks/Cargo.toml b/packages/hooks/Cargo.toml index 6a3205c68..598ffedab 100644 --- a/packages/hooks/Cargo.toml +++ b/packages/hooks/Cargo.toml @@ -20,3 +20,4 @@ log = { workspace = true } futures-util = { workspace = true, default-features = false } dioxus-core = { workspace = true } dioxus = { workspace = true } +web-sys = { version = "0.3.64", features = ["Document", "Window", "Element"] } diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index 21795a9d6..58b6c82f5 100644 --- a/packages/hooks/src/lib.rs +++ b/packages/hooks/src/lib.rs @@ -50,6 +50,9 @@ macro_rules! to_owned { }; } +mod use_on_unmount; +pub use use_on_unmount::*; + mod usecontext; pub use usecontext::*; diff --git a/packages/hooks/src/use_on_unmount.rs b/packages/hooks/src/use_on_unmount.rs new file mode 100644 index 000000000..1fa4449c1 --- /dev/null +++ b/packages/hooks/src/use_on_unmount.rs @@ -0,0 +1,74 @@ +/// Creats a callback that will be run before the component is removed. This can be used to clean up side effects from the component (created with use_effect) +/// +/// Example: +/// ```rust +/// use dioxus::prelude::*; + +/// fn app(cx: Scope) -> Element { +/// let state = use_state(cx, || true); +/// render! { +/// for _ in 0..100 { +/// h1 { +/// "spacer" +/// } +/// } +/// if **state { +/// render! { +/// child_component {} +/// } +/// } +/// button { +/// onclick: move |_| { +/// state.set(!*state.get()); +/// }, +/// "Unmount element" +/// } +/// } +/// } + +/// fn child_component(cx: Scope) -> Element { +/// let original_scroll_position = use_state(cx, || 0.0); +/// use_effect(cx, (), move |_| { +/// to_owned![original_scroll_position]; +/// async move { +/// let window = web_sys::window().unwrap(); +/// let document = window.document().unwrap(); +/// let element = document.get_element_by_id("my_element").unwrap(); +/// element.scroll_into_view(); +/// original_scroll_position.set(window.scroll_y().unwrap()); +/// } +/// }); + +/// use_on_unmount(cx, { +/// to_owned![original_scroll_position]; +/// /// restore scroll to the top of the page +/// move || { +/// let window = web_sys::window().unwrap(); +/// window.scroll_with_x_and_y(*original_scroll_position.current(), 0.0); +/// } +/// }); + +/// render!{ +/// div { +/// id: "my_element", +/// "hello" +/// } +/// } +/// } +/// ``` +pub fn use_on_unmount(cx: &dioxus_core::ScopeState, destroy: D) { + cx.use_hook(|| LifeCycle { + ondestroy: Some(destroy), + }); +} + +struct LifeCycle { + ondestroy: Option, +} + +impl Drop for LifeCycle { + fn drop(&mut self) { + let f = self.ondestroy.take().unwrap(); + f(); + } +}