From 09dee407c7cf5272db540efa358923325ee8a4b8 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Fri, 4 Aug 2023 15:58:03 +0100 Subject: [PATCH] migrated away from CloneableFn... traits --- CHANGELOG.md | 5 + Cargo.toml | 2 +- src/storage/shared.rs | 8 +- src/storage/use_storage.rs | 125 ++++++++++--------- src/use_color_mode.rs | 28 ++--- src/use_debounce_fn.rs | 12 +- src/use_draggable.rs | 20 +-- src/use_drop_zone.rs | 38 ++++-- src/use_interval.rs | 9 +- src/use_media_query.rs | 19 ++- src/use_scroll.rs | 14 +-- src/use_throttle_fn.rs | 12 +- src/use_websocket.rs | 70 ++++++----- src/utils/clonable_fn/mod.rs | 43 ------- src/utils/clonable_fn/mut_.rs | 32 ----- src/utils/clonable_fn/mut_with_arg.rs | 36 ------ src/utils/clonable_fn/with_arg.rs | 36 ------ src/utils/clonable_fn/with_arg_and_return.rs | 38 ------ src/utils/clonable_fn/with_return.rs | 37 ------ src/utils/filters/debounce.rs | 5 +- src/utils/filters/mod.rs | 29 +++-- src/utils/filters/throttle.rs | 5 +- src/utils/mod.rs | 2 - 23 files changed, 210 insertions(+), 415 deletions(-) delete mode 100644 src/utils/clonable_fn/mod.rs delete mode 100644 src/utils/clonable_fn/mut_.rs delete mode 100644 src/utils/clonable_fn/mut_with_arg.rs delete mode 100644 src/utils/clonable_fn/with_arg.rs delete mode 100644 src/utils/clonable_fn/with_arg_and_return.rs delete mode 100644 src/utils/clonable_fn/with_return.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d03b1..14ffae0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,9 +26,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The options `reconnect_limit` and `reconnect_interval` now take a `u64` instead of `Option` to improve DX. - The option `manual` has been renamed to `immediate` to make it more consistent with other functions. To port please note that `immediate` is the inverse of `manual` (`immediate` = `!manual`). +- `use_color_mode`: + - The optional `on_changed` handler parameters have changed slightly. Please refer to the docs for more details. +- Throttled or debounced functions cannot be `FnOnce` anymore. +- All traits `ClonableFn...` have been removed. ### Other Changes 🔥 +- Callbacks in options don't require to be cloneable anymore - Callback in `use_raf_fn` doesn't require to be cloneable anymore ## [0.6.2] - 2023-08-03 diff --git a/Cargo.toml b/Cargo.toml index f06f7d1..d890914 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ homepage = "https://leptos-use.rs" leptos = "0.5.0-alpha" wasm-bindgen = "0.2" js-sys = "0.3" -default-struct-builder = "0.4" +default-struct-builder = "0.5" num = { version = "0.4", optional = true } serde = { version = "1", optional = true } serde_json = { version = "1", optional = true } diff --git a/src/storage/shared.rs b/src/storage/shared.rs index 2eb8233..bd9ef93 100644 --- a/src/storage/shared.rs +++ b/src/storage/shared.rs @@ -1,8 +1,9 @@ use crate::filter_builder_methods; use crate::storage::{StorageType, UseStorageError, UseStorageOptions}; -use crate::utils::{CloneableFnWithArg, DebounceOptions, FilterOptions, ThrottleOptions}; +use crate::utils::{DebounceOptions, FilterOptions, ThrottleOptions}; use default_struct_builder::DefaultBuilder; use leptos::*; +use std::rc::Rc; macro_rules! use_specific_storage { ($(#[$outer:meta])* @@ -58,8 +59,7 @@ pub struct UseSpecificStorageOptions { /// Defaults to simply returning the stored value. merge_defaults: fn(&str, &T) -> String, /// Optional callback whenever an error occurs. The callback takes an argument of type [`UseStorageError`]. - #[builder(into)] - on_error: Box>, + on_error: Rc, /// Debounce or throttle the writing to storage whenever the value changes. filter: FilterOptions, @@ -71,7 +71,7 @@ impl Default for UseSpecificStorageOptions { listen_to_storage_changes: true, write_defaults: true, merge_defaults: |stored_value, _default_value| stored_value.to_string(), - on_error: Box::new(|_| ()), + on_error: Rc::new(|_| ()), filter: Default::default(), } } diff --git a/src/storage/use_storage.rs b/src/storage/use_storage.rs index 7a8cc4c..f5c61e1 100644 --- a/src/storage/use_storage.rs +++ b/src/storage/use_storage.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))] use crate::core::MaybeRwSignal; -use crate::utils::{CloneableFn, CloneableFnWithArg, FilterOptions}; +use crate::utils::FilterOptions; use crate::{ filter_builder_methods, use_event_listener, watch_pausable_with_options, DebounceOptions, ThrottleOptions, WatchOptions, WatchPausableReturn, @@ -12,6 +12,7 @@ use js_sys::Reflect; use leptos::*; use serde::{Deserialize, Serialize}; use serde_json::Error; +use std::rc::Rc; use std::time::Duration; use wasm_bindgen::{JsCast, JsValue}; @@ -190,18 +191,18 @@ where let raw_init = data.get_untracked(); cfg_if! { if #[cfg(feature = "ssr")] { - let remove: Box = Box::new(|| {}); + let remove: Rc = Rc::new(|| {}); } else { let storage = storage_type.into_storage(); - let remove: Box = match storage { + let remove: Rc = match storage { Ok(Some(storage)) => { let write = { let on_error = on_error.clone(); let storage = storage.clone(); let key = key.to_string(); - move |v: &T| { + Rc::new(move |v: &T| { match serde_json::to_string(&v) { Ok(ref serialized) => match storage.get_item(&key) { Ok(old_value) => { @@ -240,7 +241,7 @@ where on_error.clone()(UseStorageError::SerializationError(e)); } } - } + }) }; let read = { @@ -249,51 +250,53 @@ where let key = key.to_string(); let raw_init = raw_init.clone(); - move |event_detail: Option| -> Option { - let serialized_init = match serde_json::to_string(&raw_init) { - Ok(serialized) => Some(serialized), - Err(e) => { - on_error.clone()(UseStorageError::DefaultSerializationError(e)); - None - } - }; - - let raw_value = if let Some(event_detail) = event_detail { - event_detail.new_value - } else { - match storage.get_item(&key) { - Ok(raw_value) => match raw_value { - Some(raw_value) => Some(merge_defaults(&raw_value, &raw_init)), - None => serialized_init.clone(), - }, + Rc::new( + move |event_detail: Option| -> Option { + let serialized_init = match serde_json::to_string(&raw_init) { + Ok(serialized) => Some(serialized), Err(e) => { - on_error.clone()(UseStorageError::StorageAccessError(e)); + on_error.clone()(UseStorageError::DefaultSerializationError(e)); None } - } - }; + }; - match raw_value { - Some(raw_value) => match serde_json::from_str(&raw_value) { - Ok(v) => Some(v), - Err(e) => { - on_error.clone()(UseStorageError::SerializationError(e)); - None - } - }, - None => { - if let Some(serialized_init) = &serialized_init { - if write_defaults { - if let Err(e) = storage.set_item(&key, serialized_init) { - on_error(UseStorageError::StorageAccessError(e)); - } + let raw_value = if let Some(event_detail) = event_detail { + event_detail.new_value + } else { + match storage.get_item(&key) { + Ok(raw_value) => match raw_value { + Some(raw_value) => Some(merge_defaults(&raw_value, &raw_init)), + None => serialized_init.clone(), + }, + Err(e) => { + on_error.clone()(UseStorageError::StorageAccessError(e)); + None } } + }; - Some(raw_init) + match raw_value { + Some(raw_value) => match serde_json::from_str(&raw_value) { + Ok(v) => Some(v), + Err(e) => { + on_error.clone()(UseStorageError::SerializationError(e)); + None + } + }, + None => { + if let Some(serialized_init) = &serialized_init { + if write_defaults { + if let Err(e) = storage.set_item(&key, serialized_init) { + on_error(UseStorageError::StorageAccessError(e)); + } + } + } + + Some(raw_init.clone()) + } } - } - } + }, + ) }; let WatchPausableReturn { @@ -311,15 +314,15 @@ where let storage = storage.clone(); let raw_init = raw_init.clone(); - move |event_detail: Option| { + Rc::new(move |event_detail: Option| { if let Some(event_detail) = &event_detail { - if event_detail.storage_area != Some(storage) { + if event_detail.storage_area != Some(storage.clone()) { return; } match &event_detail.key { None => { - set_data.set(raw_init); + set_data.set(raw_init.clone()); return; } Some(event_key) => { @@ -343,21 +346,25 @@ where } else { resume_watch(); } - } + }) }; - let upd = update.clone(); - let update_from_custom_event = - move |event: web_sys::CustomEvent| upd.clone()(Some(event.into())); + let update_from_custom_event = { + let update = Rc::clone(&update); - let upd = update.clone(); - let update_from_storage_event = - move |event: web_sys::StorageEvent| upd.clone()(Some(event.into())); + move |event: web_sys::CustomEvent| update(Some(event.into())) + }; + + let update_from_storage_event = { + let update = Rc::clone(&update); + + move |event: web_sys::StorageEvent| update(Some(event.into())) + }; if listen_to_storage_changes { let _ = use_event_listener(window(), ev::storage, update_from_storage_event); let _ = use_event_listener( - window(), + window(), ev::Custom::new(CUSTOM_STORAGE_EVENT_NAME), update_from_custom_event, ); @@ -367,22 +374,22 @@ where let k = key.to_string(); - Box::new(move || { + Rc::new(move || { let _ = storage.remove_item(&k); }) } Err(e) => { on_error(UseStorageError::NoStorage(e)); - Box::new(move || {}) + Rc::new(move || {}) } _ => { // do nothing - Box::new(move || {}) + Rc::new(move || {}) } }; }} - (data, set_data, move || remove.clone()()) + (data, set_data, move || remove()) } #[derive(Clone)] @@ -461,7 +468,7 @@ pub struct UseStorageOptions { /// Defaults to simply returning the stored value. pub(crate) merge_defaults: fn(&str, &T) -> String, /// Optional callback whenever an error occurs. The callback takes an argument of type [`UseStorageError`]. - pub(crate) on_error: Box>, + pub(crate) on_error: Rc, /// Debounce or throttle the writing to storage whenever the value changes. pub(crate) filter: FilterOptions, @@ -474,7 +481,7 @@ impl Default for UseStorageOptions { listen_to_storage_changes: true, write_defaults: true, merge_defaults: |stored_value, _default_value| stored_value.to_string(), - on_error: Box::new(|_| ()), + on_error: Rc::new(|_| ()), filter: Default::default(), } } diff --git a/src/use_color_mode.rs b/src/use_color_mode.rs index a4f1d1c..a022045 100644 --- a/src/use_color_mode.rs +++ b/src/use_color_mode.rs @@ -7,11 +7,11 @@ use std::fmt::{Display, Formatter}; use crate::core::StorageType; use crate::use_preferred_dark; -use crate::utils::CloneableFnWithArg; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::*; use std::marker::PhantomData; +use std::rc::Rc; use wasm_bindgen::JsCast; /// Reactive color mode (dark / light / customs) with auto data persistence. @@ -217,10 +217,7 @@ where }; let on_changed = move |mode: ColorMode| { - on_changed(UseColorModeOnChangeArgs { - mode, - default_handler: Box::new(default_on_changed.clone()), - }); + on_changed(mode, Rc::new(default_on_changed.clone())); }; create_effect({ @@ -333,16 +330,6 @@ impl From for ColorMode { } } -/// Arguments to [`UseColorModeOptions::on_changed`] -#[derive(Clone)] -pub struct UseColorModeOnChangeArgs { - /// The color mode to change to. - pub mode: ColorMode, - - /// The default handler that would have been called if the `on_changed` handler had not been specified. - pub default_handler: Box>, -} - #[derive(DefaultBuilder)] pub struct UseColorModeOptions where @@ -367,7 +354,10 @@ where /// Custom handler that is called on updates. /// If specified this will override the default behavior. /// To get the default behaviour back you can call the provided `default_handler` function. - on_changed: Box>, + /// It takes two parameters: + /// - `mode: ColorMode`: The color mode to change to. + /// -`default_handler: Rc`: The default handler that would have been called if the `on_changed` handler had not been specified. + on_changed: OnChangedFn, /// When provided, `useStorage` will be skipped. /// Storage requires the *create feature* **`storage`** to be enabled. @@ -411,6 +401,8 @@ where _marker: PhantomData, } +type OnChangedFn = Rc)>; + impl Default for UseColorModeOptions<&'static str, web_sys::Element> { fn default() -> Self { Self { @@ -418,9 +410,7 @@ impl Default for UseColorModeOptions<&'static str, web_sys::Element> { attribute: "class".into(), initial_value: ColorMode::Auto.into(), custom_modes: vec![], - on_changed: Box::new(move |args: UseColorModeOnChangeArgs| { - (args.default_handler)(args.mode) - }), + on_changed: Rc::new(move |mode, default_handler| (default_handler)(mode)), storage_signal: None, storage_key: "leptos-use-color-scheme".into(), storage: StorageType::default(), diff --git a/src/use_debounce_fn.rs b/src/use_debounce_fn.rs index a80e080..d512720 100644 --- a/src/use_debounce_fn.rs +++ b/src/use_debounce_fn.rs @@ -77,7 +77,7 @@ pub fn use_debounce_fn( ms: impl Into> + 'static, ) -> impl Fn() -> Rc>> + Clone where - F: FnOnce() -> R + Clone + 'static, + F: Fn() -> R + Clone + 'static, R: 'static, { use_debounce_fn_with_options(func, ms, DebounceOptions::default()) @@ -90,10 +90,10 @@ pub fn use_debounce_fn_with_options( options: DebounceOptions, ) -> impl Fn() -> Rc>> + Clone where - F: FnOnce() -> R + Clone + 'static, + F: Fn() -> R + Clone + 'static, R: 'static, { - create_filter_wrapper(Box::new(debounce_filter(ms, options)), func) + create_filter_wrapper(Rc::new(debounce_filter(ms, options)), func) } /// Version of [`use_debounce_fn`] with an argument for the debounced function. See the docs for [`use_debounce_fn`] for how to use. @@ -102,7 +102,7 @@ pub fn use_debounce_fn_with_arg( ms: impl Into> + 'static, ) -> impl Fn(Arg) -> Rc>> + Clone where - F: FnOnce(Arg) -> R + Clone + 'static, + F: Fn(Arg) -> R + Clone + 'static, Arg: Clone + 'static, R: 'static, { @@ -116,9 +116,9 @@ pub fn use_debounce_fn_with_arg_and_options( options: DebounceOptions, ) -> impl Fn(Arg) -> Rc>> + Clone where - F: FnOnce(Arg) -> R + Clone + 'static, + F: Fn(Arg) -> R + Clone + 'static, Arg: Clone + 'static, R: 'static, { - create_filter_wrapper_with_arg(Box::new(debounce_filter(ms, options)), func) + create_filter_wrapper_with_arg(Rc::new(debounce_filter(ms, options)), func) } diff --git a/src/use_draggable.rs b/src/use_draggable.rs index 2e1b21c..c882949 100644 --- a/src/use_draggable.rs +++ b/src/use_draggable.rs @@ -1,10 +1,10 @@ use crate::core::{ElementMaybeSignal, MaybeRwSignal, PointerType, Position}; use crate::use_event_listener_with_options; -use crate::utils::{CloneableFnMutWithArg, CloneableFnWithArgAndReturn}; use default_struct_builder::DefaultBuilder; use leptos::ev::{pointerdown, pointermove, pointerup}; use leptos::*; use std::marker::PhantomData; +use std::rc::Rc; use wasm_bindgen::JsCast; use web_sys::PointerEvent; @@ -134,7 +134,7 @@ where x: event.client_x() as f64 - rect.left(), y: event.client_y() as f64 - rect.top(), }; - if !on_start.clone()(UseDraggableCallbackArgs { + if !on_start(UseDraggableCallbackArgs { position, event: event.clone(), }) { @@ -159,7 +159,7 @@ where y: event.client_y() as f64 - start_position.y, }; set_position.set(position); - on_move.clone()(UseDraggableCallbackArgs { + on_move(UseDraggableCallbackArgs { position, event: event.clone(), }); @@ -176,7 +176,7 @@ where return; } set_start_position.set(None); - on_end.clone()(UseDraggableCallbackArgs { + on_end(UseDraggableCallbackArgs { position: position.get_untracked(), event: event.clone(), }); @@ -253,13 +253,13 @@ where initial_value: MaybeRwSignal, /// Callback when the dragging starts. Return `false` to prevent dragging. - on_start: Box>, + on_start: Rc bool>, /// Callback during dragging. - on_move: Box>, + on_move: Rc, /// Callback when dragging end. - on_end: Box>, + on_end: Rc, #[builder(skip)] _marker1: PhantomData, @@ -284,9 +284,9 @@ impl Default handle: None, pointer_types: vec![PointerType::Mouse, PointerType::Touch, PointerType::Pen], initial_value: MaybeRwSignal::default(), - on_start: Box::new(|_| true), - on_move: Box::new(|_| {}), - on_end: Box::new(|_| {}), + on_start: Rc::new(|_| true), + on_move: Rc::new(|_| {}), + on_end: Rc::new(|_| {}), _marker1: PhantomData, _marker2: PhantomData, } diff --git a/src/use_drop_zone.rs b/src/use_drop_zone.rs index 964d4be..164c152 100644 --- a/src/use_drop_zone.rs +++ b/src/use_drop_zone.rs @@ -1,9 +1,10 @@ use crate::core::ElementMaybeSignal; use crate::use_event_listener; -use crate::utils::CloneableFnMutWithArg; use default_struct_builder::DefaultBuilder; use leptos::ev::{dragenter, dragleave, dragover, drop}; use leptos::*; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; /// Create a zone where files can be dropped. /// @@ -65,10 +66,10 @@ where T: Into + Clone + 'static, { let UseDropZoneOptions { - mut on_drop, - mut on_enter, - mut on_leave, - mut on_over, + on_drop, + on_enter, + on_leave, + on_over, } = options; let (is_over_drop_zone, set_over_drop_zone) = create_signal(false); @@ -147,16 +148,33 @@ where } /// Options for [`use_drop_zone_with_options`]. -#[derive(DefaultBuilder, Default, Clone, Debug)] +#[derive(DefaultBuilder, Clone)] pub struct UseDropZoneOptions { /// Event handler for the [`drop`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event) event - on_drop: Box>, + on_drop: Rc, /// Event handler for the [`dragenter`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event) event - on_enter: Box>, + on_enter: Rc, /// Event handler for the [`dragleave`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragleave_event) event - on_leave: Box>, + on_leave: Rc, /// Event handler for the [`dragover`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event) event - on_over: Box>, + on_over: Rc, +} + +impl Default for UseDropZoneOptions { + fn default() -> Self { + Self { + on_drop: Rc::new(|_| {}), + on_enter: Rc::new(|_| {}), + on_leave: Rc::new(|_| {}), + on_over: Rc::new(|_| {}), + } + } +} + +impl Debug for UseDropZoneOptions { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "UseDropZoneOptions") + } } /// Event passed as argument to the event handler functions of `UseDropZoneOptions`. diff --git a/src/use_interval.rs b/src/use_interval.rs index 244a321..eb39926 100644 --- a/src/use_interval.rs +++ b/src/use_interval.rs @@ -1,6 +1,7 @@ -use crate::utils::{CloneableFnWithArg, Pausable}; +use crate::utils::Pausable; use crate::{use_interval_fn_with_options, UseIntervalFnOptions}; use default_struct_builder::DefaultBuilder; +use std::rc::Rc; use leptos::*; @@ -61,7 +62,7 @@ where let cb = move || { update(); - callback.clone()(counter.get()); + callback(counter.get()); }; let Pausable { @@ -93,14 +94,14 @@ pub struct UseIntervalOptions { immediate: bool, /// Callback on every interval. - callback: Box>, + callback: Rc, } impl Default for UseIntervalOptions { fn default() -> Self { Self { immediate: true, - callback: Box::new(|_: u64| {}), + callback: Rc::new(|_: u64| {}), } } } diff --git a/src/use_media_query.rs b/src/use_media_query.rs index 3363276..b9c5be8 100644 --- a/src/use_media_query.rs +++ b/src/use_media_query.rs @@ -1,7 +1,6 @@ #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))] use crate::use_event_listener; -use crate::utils::CloneableFnMutWithArg; use cfg_if::cfg_if; use leptos::ev::change; use leptos::*; @@ -48,8 +47,7 @@ pub fn use_media_query(query: impl Into>) -> Signal { let media_query: Rc>> = Rc::new(RefCell::new(None)); let remove_listener: RemoveListener = Rc::new(RefCell::new(None)); - let listener: Rc>>> = - Rc::new(RefCell::new(Box::new(|_| {}))); + let listener = Rc::new(RefCell::new(Rc::new(|_| {}) as Rc)); let cleanup = { let remove_listener = Rc::clone(&remove_listener); @@ -65,7 +63,7 @@ pub fn use_media_query(query: impl Into>) -> Signal { let cleanup = cleanup.clone(); let listener = Rc::clone(&listener); - move || { + Rc::new(move || { cleanup(); let mut media_query = media_query.borrow_mut(); @@ -74,21 +72,22 @@ pub fn use_media_query(query: impl Into>) -> Signal { if let Some(media_query) = media_query.as_ref() { set_matches.set(media_query.matches()); + let listener = Rc::clone(&*listener.borrow()); + remove_listener.replace(Some(Box::new(use_event_listener( - media_query.clone(), + media_query.clone(), change, - listener.borrow().clone(), + move |e| listener(e), )))); } else { set_matches.set(false); } - } + }) }; { - let update = update.clone(); - listener - .replace(Box::new(move |_| update()) as Box>); + let update = Rc::clone(&update); + listener.replace(Rc::new(move |_| update()) as Rc); } create_effect(move |_| update()); diff --git a/src/use_scroll.rs b/src/use_scroll.rs index daed2a2..c495051 100644 --- a/src/use_scroll.rs +++ b/src/use_scroll.rs @@ -1,11 +1,11 @@ use crate::core::ElementMaybeSignal; use crate::use_event_listener::use_event_listener_with_options; -use crate::utils::CloneableFnWithArg; use crate::{use_debounce_fn_with_arg, use_throttle_fn_with_arg_and_options, ThrottleOptions}; use default_struct_builder::DefaultBuilder; use leptos::ev::EventDescriptor; use leptos::*; use std::borrow::Cow; +use std::rc::Rc; use wasm_bindgen::JsCast; /// Reactive scroll position and state. @@ -232,7 +232,7 @@ where }); let on_scroll_end = { - let on_stop = options.on_stop.clone(); + let on_stop = Rc::clone(&options.on_stop); move |e| { if !is_scrolling.get_untracked() { @@ -328,7 +328,7 @@ where }; let on_scroll_handler = { - let on_scroll = options.on_scroll.clone(); + let on_scroll = Rc::clone(&options.on_scroll); move |e: web_sys::Event| { let target: web_sys::Element = event_target(&e); @@ -441,10 +441,10 @@ pub struct UseScrollOptions { offset: ScrollOffset, /// Callback when scrolling is happening. - on_scroll: Box>, + on_scroll: Rc, /// Callback when scrolling stops (after `idle` + `throttle` milliseconds have passed). - on_stop: Box>, + on_stop: Rc, /// Options passed to the `addEventListener("scroll", ...)` call event_listener_options: web_sys::AddEventListenerOptions, @@ -461,8 +461,8 @@ impl Default for UseScrollOptions { throttle: 0.0, idle: 200.0, offset: ScrollOffset::default(), - on_scroll: Box::new(|_| {}), - on_stop: Box::new(|_| {}), + on_scroll: Rc::new(|_| {}), + on_stop: Rc::new(|_| {}), event_listener_options: Default::default(), behavior: Default::default(), } diff --git a/src/use_throttle_fn.rs b/src/use_throttle_fn.rs index 985cdb8..6652248 100644 --- a/src/use_throttle_fn.rs +++ b/src/use_throttle_fn.rs @@ -73,7 +73,7 @@ pub fn use_throttle_fn( ms: impl Into> + 'static, ) -> impl Fn() -> Rc>> + Clone where - F: FnOnce() -> R + Clone + 'static, + F: Fn() -> R + Clone + 'static, R: 'static, { use_throttle_fn_with_options(func, ms, Default::default()) @@ -86,10 +86,10 @@ pub fn use_throttle_fn_with_options( options: ThrottleOptions, ) -> impl Fn() -> Rc>> + Clone where - F: FnOnce() -> R + Clone + 'static, + F: Fn() -> R + Clone + 'static, R: 'static, { - create_filter_wrapper(Box::new(throttle_filter(ms, options)), func) + create_filter_wrapper(Rc::new(throttle_filter(ms, options)), func) } /// Version of [`use_throttle_fn`] with an argument for the throttled function. See the docs for [`use_throttle_fn`] for how to use. @@ -98,7 +98,7 @@ pub fn use_throttle_fn_with_arg( ms: impl Into> + 'static, ) -> impl Fn(Arg) -> Rc>> + Clone where - F: FnOnce(Arg) -> R + Clone + 'static, + F: Fn(Arg) -> R + Clone + 'static, Arg: Clone + 'static, R: 'static, { @@ -112,9 +112,9 @@ pub fn use_throttle_fn_with_arg_and_options( options: ThrottleOptions, ) -> impl Fn(Arg) -> Rc>> + Clone where - F: FnOnce(Arg) -> R + Clone + 'static, + F: Fn(Arg) -> R + Clone + 'static, Arg: Clone + 'static, R: 'static, { - create_filter_wrapper_with_arg(Box::new(throttle_filter(ms, options)), func) + create_filter_wrapper_with_arg(Rc::new(throttle_filter(ms, options)), func) } diff --git a/src/use_websocket.rs b/src/use_websocket.rs index 0fbfa3e..098e33c 100644 --- a/src/use_websocket.rs +++ b/src/use_websocket.rs @@ -1,7 +1,6 @@ #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))] use cfg_if::cfg_if; -use core::fmt; use leptos::{leptos_dom::helpers::TimeoutHandle, *}; use std::rc::Rc; use std::time::Duration; @@ -12,8 +11,6 @@ use js_sys::Array; use wasm_bindgen::{prelude::*, JsCast, JsValue}; use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; -use crate::utils::CloneableFnWithArg; - /// Creating and managing a [Websocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) connection. /// /// ## Demo @@ -102,15 +99,24 @@ pub fn use_websocket_with_options( > { let url = url.to_string(); + let UseWebSocketOptions { + on_open, + on_message, + on_message_bytes, + on_error, + on_close, + reconnect_limit, + reconnect_interval, + immediate, + protocols, + } = options; + let (ready_state, set_ready_state) = create_signal(ConnectionReadyState::Closed); let (message, set_message) = create_signal(None); let (message_bytes, set_message_bytes) = create_signal(None); let ws_ref: StoredValue> = store_value(None); - let reconnect_limit = options.reconnect_limit; - let reconnect_timer_ref: StoredValue> = store_value(None); - let immediate = options.immediate; let reconnect_times_ref: StoredValue = store_value(0); let unmounted_ref = store_value(false); @@ -118,14 +124,6 @@ pub fn use_websocket_with_options( let connect_ref: StoredValue>> = store_value(None); cfg_if! { if #[cfg(not(feature = "ssr"))] { - let on_open_ref = store_value(options.on_open); - let on_message_ref = store_value(options.on_message); - let on_message_bytes_ref = store_value(options.on_message_bytes); - let on_error_ref = store_value(options.on_error); - let on_close_ref = store_value(options.on_close); - - let reconnect_interval = options.reconnect_interval; - let protocols = options.protocols; let reconnect_ref: StoredValue>> = store_value(None); reconnect_ref.set_value({ @@ -181,13 +179,14 @@ pub fn use_websocket_with_options( // onopen handler { + let on_open = Rc::clone(&on_open); + let onopen_closure = Closure::wrap(Box::new(move |e: Event| { if unmounted_ref.get_value() { return; } - let callback = on_open_ref.get_value(); - callback(e); + on_open(e); set_ready_state.set(ConnectionReadyState::Open); }) as Box); @@ -198,6 +197,9 @@ pub fn use_websocket_with_options( // onmessage handler { + let on_message = Rc::clone(&on_message); + let on_message_bytes = Rc::clone(&on_message_bytes); + let onmessage_closure = Closure::wrap(Box::new(move |e: MessageEvent| { if unmounted_ref.get_value() { return; @@ -211,8 +213,7 @@ pub fn use_websocket_with_options( }, |txt| { let txt = String::from(&txt); - let callback = on_message_ref.get_value(); - callback(txt.clone()); + on_message(txt.clone()); set_message.set(Some(txt)); }, @@ -221,8 +222,7 @@ pub fn use_websocket_with_options( |array_buffer| { let array = js_sys::Uint8Array::new(&array_buffer); let array = array.to_vec(); - let callback = on_message_bytes_ref.get_value(); - callback(array.clone()); + on_message_bytes(array.clone()); set_message_bytes.set(Some(array)); }, @@ -235,6 +235,8 @@ pub fn use_websocket_with_options( // onerror handler { + let on_error = Rc::clone(&on_error); + let onerror_closure = Closure::wrap(Box::new(move |e: Event| { if unmounted_ref.get_value() { return; @@ -244,8 +246,7 @@ pub fn use_websocket_with_options( reconnect(); } - let callback = on_error_ref.get_value(); - callback(e); + on_error(e); set_ready_state.set(ConnectionReadyState::Closed); }) as Box); @@ -255,6 +256,8 @@ pub fn use_websocket_with_options( // onclose handler { + let on_close = Rc::clone(&on_close); + let onclose_closure = Closure::wrap(Box::new(move |e: CloseEvent| { if unmounted_ref.get_value() { return; @@ -264,8 +267,7 @@ pub fn use_websocket_with_options( reconnect(); } - let callback = on_close_ref.get_value(); - callback(e); + on_close(e); set_ready_state.set(ConnectionReadyState::Closed); }) @@ -348,15 +350,15 @@ pub fn use_websocket_with_options( #[derive(DefaultBuilder)] pub struct UseWebSocketOptions { /// `WebSocket` connect callback. - on_open: Box>, + on_open: Rc, /// `WebSocket` message callback for text. - on_message: Box>, + on_message: Rc, /// `WebSocket` message callback for binary. - on_message_bytes: Box>>, + on_message_bytes: Rc)>, /// `WebSocket` error callback. - on_error: Box>, + on_error: Rc, /// `WebSocket` close callback. - on_close: Box>, + on_close: Rc, /// Retry times. Defaults to 3. reconnect_limit: u64, /// Retry interval in ms. Defaults to 3000. @@ -372,11 +374,11 @@ pub struct UseWebSocketOptions { impl Default for UseWebSocketOptions { fn default() -> Self { Self { - on_open: Box::new(|_| {}), - on_message: Box::new(|_| {}), - on_message_bytes: Box::new(|_| {}), - on_error: Box::new(|_| {}), - on_close: Box::new(|_| {}), + on_open: Rc::new(|_| {}), + on_message: Rc::new(|_| {}), + on_message_bytes: Rc::new(|_| {}), + on_error: Rc::new(|_| {}), + on_close: Rc::new(|_| {}), reconnect_limit: 3, reconnect_interval: 3000, immediate: true, diff --git a/src/utils/clonable_fn/mod.rs b/src/utils/clonable_fn/mod.rs deleted file mode 100644 index 7e05e40..0000000 --- a/src/utils/clonable_fn/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -mod mut_; -mod mut_with_arg; -mod with_arg; -mod with_arg_and_return; -mod with_return; - -pub use mut_::*; -pub use mut_with_arg::*; -use std::fmt::Debug; -pub use with_arg::*; -pub use with_arg_and_return::*; -pub use with_return::*; - -pub trait CloneableFn: FnOnce() { - fn clone_box(&self) -> Box; -} - -impl CloneableFn for F -where - F: FnOnce() + Clone + 'static, -{ - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Self { - (**self).clone_box() - } -} - -impl Default for Box { - fn default() -> Self { - Box::new(|| {}) - } -} - -impl Debug for Box { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Box",) - } -} diff --git a/src/utils/clonable_fn/mut_.rs b/src/utils/clonable_fn/mut_.rs deleted file mode 100644 index f2a16b2..0000000 --- a/src/utils/clonable_fn/mut_.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::fmt::Debug; - -pub trait CloneableFnMut: FnMut() { - fn clone_box(&self) -> Box; -} - -impl CloneableFnMut for F -where - F: FnMut() + Clone + 'static, -{ - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Self { - (**self).clone_box() - } -} - -impl Default for Box { - fn default() -> Self { - Box::new(|| {}) - } -} - -impl Debug for Box { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Box",) - } -} diff --git a/src/utils/clonable_fn/mut_with_arg.rs b/src/utils/clonable_fn/mut_with_arg.rs deleted file mode 100644 index 3d8fb63..0000000 --- a/src/utils/clonable_fn/mut_with_arg.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::fmt::Debug; - -pub trait CloneableFnMutWithArg: FnMut(Arg) { - fn clone_box(&self) -> Box>; -} - -impl CloneableFnMutWithArg for F -where - F: FnMut(Arg) + Clone + 'static, -{ - fn clone_box(&self) -> Box> { - Box::new(self.clone()) - } -} - -impl Clone for Box> { - fn clone(&self) -> Self { - (**self).clone_box() - } -} - -impl Default for Box> { - fn default() -> Self { - Box::new(|_| {}) - } -} - -impl Debug for Box> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Box>", - std::any::type_name::() - ) - } -} diff --git a/src/utils/clonable_fn/with_arg.rs b/src/utils/clonable_fn/with_arg.rs deleted file mode 100644 index fedcf57..0000000 --- a/src/utils/clonable_fn/with_arg.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::fmt::Debug; - -pub trait CloneableFnWithArg: FnOnce(Arg) { - fn clone_box(&self) -> Box>; -} - -impl CloneableFnWithArg for F -where - F: FnOnce(Arg) + Clone + 'static, -{ - fn clone_box(&self) -> Box> { - Box::new(self.clone()) - } -} - -impl Clone for Box> { - fn clone(&self) -> Self { - (**self).clone_box() - } -} - -impl Default for Box> { - fn default() -> Self { - Box::new(|_| {}) - } -} - -impl Debug for Box> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Box>", - std::any::type_name::() - ) - } -} diff --git a/src/utils/clonable_fn/with_arg_and_return.rs b/src/utils/clonable_fn/with_arg_and_return.rs deleted file mode 100644 index b9a4122..0000000 --- a/src/utils/clonable_fn/with_arg_and_return.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::fmt::Debug; - -pub trait CloneableFnWithArgAndReturn: FnOnce(Arg) -> R { - fn clone_box(&self) -> Box>; -} - -impl CloneableFnWithArgAndReturn for F -where - F: FnMut(Arg) -> R + Clone + 'static, - R: 'static, -{ - fn clone_box(&self) -> Box> { - Box::new(self.clone()) - } -} - -impl Clone for Box> { - fn clone(&self) -> Self { - (**self).clone_box() - } -} - -impl Default for Box> { - fn default() -> Self { - Box::new(|_| Default::default()) - } -} - -impl Debug for Box> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Box>", - std::any::type_name::(), - std::any::type_name::() - ) - } -} diff --git a/src/utils/clonable_fn/with_return.rs b/src/utils/clonable_fn/with_return.rs deleted file mode 100644 index 4deb656..0000000 --- a/src/utils/clonable_fn/with_return.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::fmt::Debug; - -pub trait CloneableFnWithReturn: FnOnce() -> R { - fn clone_box(&self) -> Box>; -} - -impl CloneableFnWithReturn for F -where - F: FnOnce() -> R + Clone + 'static, - R: 'static, -{ - fn clone_box(&self) -> Box> { - Box::new(self.clone()) - } -} - -impl Clone for Box> { - fn clone(&self) -> Self { - (**self).clone_box() - } -} - -impl Default for Box> { - fn default() -> Self { - Box::new(|| Default::default()) - } -} - -impl Debug for Box> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Box>", - std::any::type_name::() - ) - } -} diff --git a/src/utils/filters/debounce.rs b/src/utils/filters/debounce.rs index b33901b..6186960 100644 --- a/src/utils/filters/debounce.rs +++ b/src/utils/filters/debounce.rs @@ -1,6 +1,5 @@ #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))] -use crate::utils::CloneableFnWithReturn; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::leptos_dom::helpers::TimeoutHandle; @@ -20,7 +19,7 @@ pub struct DebounceOptions { pub fn debounce_filter( ms: impl Into>, options: DebounceOptions, -) -> impl Fn(Box>) -> Rc>> + Clone +) -> impl Fn(Rc R>) -> Rc>> + Clone where R: 'static, { @@ -38,7 +37,7 @@ where let ms = ms.into(); let max_wait_signal = options.max_wait; - move |_invoke: Box>| { + move |_invoke: Rc R>| { let duration = ms.get_untracked(); let max_duration = max_wait_signal.get_untracked(); diff --git a/src/utils/filters/mod.rs b/src/utils/filters/mod.rs index 5d33059..7607d33 100644 --- a/src/utils/filters/mod.rs +++ b/src/utils/filters/mod.rs @@ -4,40 +4,39 @@ mod throttle; pub use debounce::*; pub use throttle::*; -use crate::utils::{CloneableFnWithArgAndReturn, CloneableFnWithReturn}; use leptos::MaybeSignal; use std::cell::RefCell; use std::rc::Rc; -macro_rules! BoxFilterFn { +macro_rules! RcFilterFn { ($R:ident) => { - Box>, Rc>>>> + Rc $R>) -> Rc>>> } } pub fn create_filter_wrapper( - filter: BoxFilterFn!(R), + filter: RcFilterFn!(R), func: F, ) -> impl Fn() -> Rc>> + Clone where - F: FnOnce() -> R + Clone + 'static, + F: Fn() -> R + Clone + 'static, R: 'static, { - move || filter.clone()(Box::new(func.clone())) + move || Rc::clone(&filter)(Rc::new(func.clone())) } pub fn create_filter_wrapper_with_arg( - filter: BoxFilterFn!(R), + filter: RcFilterFn!(R), func: F, ) -> impl Fn(Arg) -> Rc>> + Clone where - F: FnOnce(Arg) -> R + Clone + 'static, + F: Fn(Arg) -> R + Clone + 'static, R: 'static, Arg: Clone + 'static, { move |arg: Arg| { let func = func.clone(); - filter.clone()(Box::new(move || func(arg))) + Rc::clone(&filter)(Rc::new(move || func(arg.clone()))) } } @@ -57,16 +56,16 @@ pub enum FilterOptions { } impl FilterOptions { - pub fn filter_fn(&self) -> BoxFilterFn!(R) + pub fn filter_fn(&self) -> RcFilterFn!(R) where R: 'static, { match self { - FilterOptions::Debounce { ms, options } => Box::new(debounce_filter(*ms, *options)), - FilterOptions::Throttle { ms, options } => Box::new(throttle_filter(*ms, *options)), - FilterOptions::None => Box::new(|invoke: Box>| { - Rc::new(RefCell::new(Some(invoke()))) - }), + FilterOptions::Debounce { ms, options } => Rc::new(debounce_filter(*ms, *options)), + FilterOptions::Throttle { ms, options } => Rc::new(throttle_filter(*ms, *options)), + FilterOptions::None => { + Rc::new(|invoke: Rc R>| Rc::new(RefCell::new(Some(invoke())))) + } } } } diff --git a/src/utils/filters/throttle.rs b/src/utils/filters/throttle.rs index 48b6fc7..caaa15e 100644 --- a/src/utils/filters/throttle.rs +++ b/src/utils/filters/throttle.rs @@ -1,6 +1,5 @@ #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))] -use crate::utils::CloneableFnWithReturn; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use js_sys::Date; @@ -31,7 +30,7 @@ impl Default for ThrottleOptions { pub fn throttle_filter( ms: impl Into>, options: ThrottleOptions, -) -> impl Fn(Box>) -> Rc>> + Clone +) -> impl Fn(Rc R>) -> Rc>> + Clone where R: 'static, { @@ -50,7 +49,7 @@ where let ms = ms.into(); - move |mut _invoke: Box>| { + move |mut _invoke: Rc R>| { let duration = ms.get_untracked(); let elapsed = Date::now() - last_exec.get(); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 30625a0..404c372 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,4 +1,3 @@ -mod clonable_fn; mod filters; mod is; mod js_value_from_to_string; @@ -6,7 +5,6 @@ mod pausable; mod signal_filtered; mod use_derive_signal; -pub use clonable_fn::*; pub use filters::*; pub use is::*; pub(crate) use js_value_from_to_string::*;