From 535594f96392ddc1c49925a96e96185bfe3db0dd Mon Sep 17 00:00:00 2001 From: luoxiaozero <48741584+luoxiaozero@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:01:48 +0800 Subject: [PATCH] fix: next_frame leaks (#147) * fix: next_frame leaks * ci: added workflow path --- .github/workflows/ci.yml | 13 ++++--- .github/workflows/demo.yml | 9 ++++- thaw_components/src/css_transition/mod.rs | 23 +++++++------ thaw_utils/src/hooks/mod.rs | 2 ++ thaw_utils/src/hooks/use_next_frame.rs | 41 +++++++++++++++++++++++ thaw_utils/src/lib.rs | 4 +-- 6 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 thaw_utils/src/hooks/use_next_frame.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3b101d..c680f51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,13 +2,16 @@ name: CI on: workflow_dispatch: pull_request: - paths: ["demo/**", "demo_markdown/**", "thaw/**"] + paths: + [ + "demo/**", + "demo_markdown/**", + "thaw/**", + "thaw_components/**", + "thaw_utils/**", + ] branches: - main - push: - paths: ["demo/**", "demo_markdown/**", "thaw/**"] - branches: - - thaw/v0.2 jobs: stable: diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index 05c7c6a..6cb9278 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -2,7 +2,14 @@ name: Deploy demo on: workflow_dispatch: push: - paths: ["demo/**", "demo_markdown/**", "thaw/**"] + paths: + [ + "demo/**", + "demo_markdown/**", + "thaw/**", + "thaw_components/**", + "thaw_utils/**", + ] branches: - main diff --git a/thaw_components/src/css_transition/mod.rs b/thaw_components/src/css_transition/mod.rs index 4ebda19..a4abe9b 100644 --- a/thaw_components/src/css_transition/mod.rs +++ b/thaw_components/src/css_transition/mod.rs @@ -1,6 +1,6 @@ use leptos::{html::ElementDescriptor, *}; use std::{ops::Deref, time::Duration}; -use thaw_utils::{add_event_listener, EventListenerHandle}; +use thaw_utils::{add_event_listener, use_next_frame, EventListenerHandle}; /// # CSS Transition /// @@ -26,6 +26,7 @@ where let any_el = node_el.clone().into_any(); let el = any_el.deref().clone(); let class_list = el.class_list(); + let next_frame = use_next_frame(); let end_handle = StoredValue::new(None::); let end_count = StoredValue::new(None::); let finish = StoredValue::new(None::>); @@ -53,7 +54,7 @@ where set_timeout( move || { - finish.update_value(|v| { + finish.try_update_value(|v| { v.take().map(|f| f.call(())); }); }, @@ -107,7 +108,7 @@ where display.set(None); let class_list = class_list.clone(); - next_frame(move || { + next_frame.run(move || { let _ = class_list.remove_1(&enter_from); let _ = class_list.add_1(&enter_to); @@ -136,7 +137,7 @@ where let _ = class_list.add_2(&leave_from, &leave_active); let class_list = class_list.clone(); - next_frame(move || { + next_frame.run(move || { let _ = class_list.remove_1(&leave_from); let _ = class_list.add_1(&leave_to); @@ -170,17 +171,19 @@ where show }); + + on_cleanup(move || { + end_handle.update_value(|handle| { + if let Some(handle) = handle.take() { + handle.remove(); + } + }); + }) }); children(display.read_only()) } -fn next_frame(cb: impl FnOnce() + 'static) { - request_animation_frame(move || { - request_animation_frame(cb); - }); -} - #[derive(PartialEq)] enum AnimationTypes { Transition, diff --git a/thaw_utils/src/hooks/mod.rs b/thaw_utils/src/hooks/mod.rs index ac26180..315230d 100644 --- a/thaw_utils/src/hooks/mod.rs +++ b/thaw_utils/src/hooks/mod.rs @@ -1,5 +1,7 @@ mod use_click_position; mod use_lock_html_scroll; +mod use_next_frame; pub use use_click_position::use_click_position; pub use use_lock_html_scroll::use_lock_html_scroll; +pub use use_next_frame::{use_next_frame, NextFrame}; diff --git a/thaw_utils/src/hooks/use_next_frame.rs b/thaw_utils/src/hooks/use_next_frame.rs new file mode 100644 index 0000000..7b1a60b --- /dev/null +++ b/thaw_utils/src/hooks/use_next_frame.rs @@ -0,0 +1,41 @@ +use leptos::{ + leptos_dom::helpers::AnimationFrameRequestHandle, on_cleanup, + request_animation_frame_with_handle, StoredValue, +}; + +pub fn use_next_frame() -> NextFrame { + let next_frame = NextFrame::default(); + + on_cleanup(move || { + next_frame.cancel(); + }); + + next_frame +} + +#[derive(Default, Clone)] +pub struct NextFrame(StoredValue>); + +impl Copy for NextFrame {} + +impl NextFrame { + pub fn run(&self, cb: impl FnOnce() + 'static) { + self.cancel(); + + let next_frame_hadnle = self.0.clone(); + let handle = request_animation_frame_with_handle(move || { + let handle = request_animation_frame_with_handle(cb).unwrap(); + next_frame_hadnle.set_value(Some(handle)); + }) + .unwrap(); + self.0.set_value(Some(handle)); + } + + pub fn cancel(&self) { + self.0.update_value(|value| { + if let Some(value) = value.take() { + value.cancel(); + } + }); + } +} diff --git a/thaw_utils/src/lib.rs b/thaw_utils/src/lib.rs index 58f7157..2edc3f5 100644 --- a/thaw_utils/src/lib.rs +++ b/thaw_utils/src/lib.rs @@ -7,13 +7,13 @@ mod signals; mod time; pub use event_listener::{add_event_listener, EventListenerHandle}; -pub use hooks::{use_click_position, use_lock_html_scroll}; +pub use hooks::{use_click_position, use_lock_html_scroll, use_next_frame, NextFrame}; pub use mount_style::mount_style; pub use optional_prop::OptionalProp; pub use signals::{ create_component_ref, ComponentRef, Model, OptionalMaybeSignal, SignalWatch, StoredMaybeSignal, }; -pub use time::*; +pub use time::now_date; pub fn with_hydration_off(f: impl FnOnce() -> T) -> T { #[cfg(feature = "hydrate")]