use crate::filter_builder_methods; use crate::utils::{create_filter_wrapper, DebounceOptions, FilterOptions, ThrottleOptions}; use default_struct_builder::DefaultBuilder; use leptos::*; use std::cell::RefCell; use std::rc::Rc; /// A version of `leptos::watch` but with additional options. /// /// ## Immediate /// /// This is the same as for `leptos::watch`. But you don't have to specify it. /// By default its set to `false`. /// If `immediate` is `true`, the `callback` will run immediately (this is also true if throttled/debounced). /// If it's `false`, the `callback` will run only after /// the first change is detected of any signal that is accessed in `deps`. /// /// ``` /// # use leptos::*; /// # use leptos::logging::log; /// # use leptos_use::{watch_with_options, WatchOptions}; /// # /// # pub fn Demo() -> impl IntoView { /// let (num, set_num) = create_signal(0); /// /// watch_with_options( /// move || num.get(), /// move |num, _, _| { /// log!("Number {}", num); /// }, /// WatchOptions::default().immediate(true), /// ); // > "Number 0" /// /// set_num.set(1); // > "Number 1" /// # view! { } /// # } /// ``` /// /// ## Filters /// /// The callback can be throttled or debounced. Please see [`watch_throttled`] and [`watch_debounced`] for details. /// /// ``` /// # use leptos::*; /// # use leptos::logging::log; /// # use leptos_use::{watch_with_options, WatchOptions}; /// # /// # pub fn Demo() -> impl IntoView { /// # let (num, set_num) = create_signal(0); /// # /// watch_with_options( /// move || num.get(), /// move |num, _, _| { /// log!("Number {}", num); /// }, /// WatchOptions::default().throttle(100.0), // there's also `throttle_with_options` /// ); /// # view! { } /// # } /// ``` /// /// ``` /// # use leptos::*; /// # use leptos::logging::log; /// # use leptos_use::{watch_with_options, WatchOptions}; /// # /// # pub fn Demo() -> impl IntoView { /// # let (num, set_num) = create_signal(0); /// # /// watch_with_options( /// move || num.get(), /// move |num, _, _| { /// log!("number {}", num); /// }, /// WatchOptions::default().debounce(100.0), // there's also `debounce_with_options` /// ); /// # view! { } /// # } /// ``` /// /// ## Server-Side Rendering /// /// On the server this works just fine except if you throttle or debounce in which case the callback /// will never be called except if you set `immediate` to `true` in which case the callback will be /// called exactly once when `watch()` is executed. /// /// ## See also /// /// * [`watch_throttled`] /// * [`watch_debounced`] /// Version of `watch` that accepts `WatchOptions`. See [`watch`] for how to use. pub fn watch_with_options( deps: DFn, callback: CFn, options: WatchOptions, ) -> impl Fn() + Clone where DFn: Fn() -> W + 'static, CFn: Fn(&W, Option<&W>, Option) -> T + Clone + 'static, W: Clone + 'static, T: Clone + 'static, { let cur_deps_value: Rc>> = Rc::new(RefCell::new(None)); let prev_deps_value: Rc>> = Rc::new(RefCell::new(None)); let prev_callback_value: Rc>> = Rc::new(RefCell::new(None)); let wrapped_callback = { let cur_deps_value = Rc::clone(&cur_deps_value); let prev_deps_value = Rc::clone(&prev_deps_value); let prev_callback_val = Rc::clone(&prev_callback_value); move || { #[cfg(debug_assertions)] let prev = SpecialNonReactiveZone::enter(); let ret = callback( cur_deps_value .borrow() .as_ref() .expect("this will not be called before there is deps value"), prev_deps_value.borrow().as_ref(), prev_callback_val.take(), ); #[cfg(debug_assertions)] SpecialNonReactiveZone::exit(prev); ret } }; let filtered_callback = create_filter_wrapper(options.filter.filter_fn(), wrapped_callback.clone()); leptos::watch( deps, move |deps_value, previous_deps_value, did_run_before| { cur_deps_value.replace(Some(deps_value.clone())); prev_deps_value.replace(previous_deps_value.cloned()); let callback_value = if options.immediate && did_run_before.is_none() { Some(wrapped_callback()) } else { filtered_callback().take() }; prev_callback_value.replace(callback_value); }, options.immediate, ) // create_effect(move |did_run_before| { // if !is_active.get() { // return; // } // // let deps_value = deps(); // // if !options.immediate && did_run_before.is_none() { // prev_deps_value.replace(Some(deps_value)); // return; // } // // cur_deps_value.replace(Some(deps_value.clone())); // // // prev_deps_value.replace(Some(deps_value)); // }); // // } /// Options for `watch_with_options` #[derive(DefaultBuilder, Default)] pub struct WatchOptions { /// If `immediate` is true, the `callback` will run immediately. /// If it's `false, the `callback` will run only after /// the first change is detected of any signal that is accessed in `deps`. /// Defaults to `false`. immediate: bool, /// Allows to debounce or throttle the callback. Defaults to no filter. filter: FilterOptions, } impl WatchOptions { filter_builder_methods!( /// the watch callback filter ); } #[deprecated(since = "0.7.0", note = "Use `leptos::watch` instead")] #[inline(always)] pub fn watch(deps: DFn, callback: CFn) -> impl Fn() + Clone where DFn: Fn() -> W + 'static, CFn: Fn(&W, Option<&W>, Option) -> T + Clone + 'static, W: Clone + 'static, T: Clone + 'static, { leptos::watch(deps, callback, false) }