Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,15 @@ codee = { version = "0.3", features = [
"prost",
] }
getrandom = { version = "0.4", features = ["wasm_js"] }
gloo-timers = { version = "0.4", features = ["futures"] }
leptos_meta = "0.8"
rand = "0.10"
serde = { version = "1", features = ["derive"] }
unic-langid = { version = "0.9", features = ["macros"] }

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3"

[features]
default = [
"is_err",
Expand Down
9 changes: 9 additions & 0 deletions src/use_timeout_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ use std::marker::PhantomData;
/// # }
/// ```
///
/// ## Rescheduling
///
/// Calling `start` while a previous timer is still pending cancels that pending
/// timer and schedules a fresh one. Only the most recent `start` call's
/// callback will fire. This matches VueUse's `useTimeoutFn` semantics.
///
/// ## SendWrapped Return
///
/// The returned closures `start` and `stop` are sendwrapped functions. They can
Expand Down Expand Up @@ -87,8 +93,10 @@ where
start = {
let timer = Arc::clone(&timer);
let callback = callback.clone();
let clear = clear.clone();

sendwrap_fn!(move |arg: Arg| {
clear();
set_pending.set(true);

let handle = set_timeout_with_handle(
Expand Down Expand Up @@ -145,6 +153,7 @@ where
pub is_pending: Signal<bool>,

/// Start the timeout. The `callback` will be called after `delay` milliseconds.
/// If a previous timer is still pending, it is cancelled and replaced.
pub start: StartFn,

/// Stop the timeout. If the timeout was still pending the `callback` is not called.
Expand Down
45 changes: 45 additions & 0 deletions tests/use_timeout_fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#![cfg(target_arch = "wasm32")]

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

use gloo_timers::future::TimeoutFuture;
use leptos::prelude::*;
use leptos_use::{UseTimeoutFnReturn, use_timeout_fn};
use wasm_bindgen_test::*;

wasm_bindgen_test_configure!(run_in_browser);

// Regression test for https://github.com/Synphonyte/leptos-use/issues/306:
// Calling `start()` while a previous timer is still pending must cancel the
// previous timer (matching VueUse's `useTimeoutFn`). Before the fix, both
// timers ran to completion and the original callback fired at its
// originally-scheduled time.
#[wasm_bindgen_test]
async fn start_cancels_pending_timer() {
let owner = Owner::new();
owner.set();

let count = Arc::new(AtomicUsize::new(0));

let UseTimeoutFnReturn { start, .. } = use_timeout_fn(
{
let count = Arc::clone(&count);
move |_: ()| {
count.fetch_add(1, Ordering::SeqCst);
}
},
200.0_f64,
);

start(());
TimeoutFuture::new(50).await;
start(());
TimeoutFuture::new(400).await;

assert_eq!(
count.load(Ordering::SeqCst),
1,
"second start() must cancel the first timer; both callbacks fired"
);
}
Loading