From 9badfa997b1aae4dbd7cd58dedbadbd6926ddcfd Mon Sep 17 00:00:00 2001 From: Fabian Keller Date: Sun, 23 Apr 2023 21:27:47 +0200 Subject: [PATCH] examples: add timer example with reactive `use_interval` hook (#925) --- examples/Makefile.toml | 1 + examples/timer/Cargo.toml | 24 ++++++++++++ examples/timer/Makefile.toml | 9 +++++ examples/timer/README.md | 7 ++++ examples/timer/index.html | 8 ++++ examples/timer/public/favicon.ico | Bin 0 -> 15406 bytes examples/timer/rust-toolchain.toml | 2 + examples/timer/src/lib.rs | 61 +++++++++++++++++++++++++++++ examples/timer/src/main.rs | 12 ++++++ 9 files changed, 124 insertions(+) create mode 100644 examples/timer/Cargo.toml create mode 100644 examples/timer/Makefile.toml create mode 100644 examples/timer/README.md create mode 100644 examples/timer/index.html create mode 100644 examples/timer/public/favicon.ico create mode 100644 examples/timer/rust-toolchain.toml create mode 100644 examples/timer/src/lib.rs create mode 100644 examples/timer/src/main.rs diff --git a/examples/Makefile.toml b/examples/Makefile.toml index a6265d8d1..1a741ca2b 100644 --- a/examples/Makefile.toml +++ b/examples/Makefile.toml @@ -24,6 +24,7 @@ CARGO_MAKE_CRATE_WORKSPACE_MEMBERS = [ "ssr_modes_axum", "tailwind", "tailwind_csr_trunk", + "timer", "todo_app_sqlite", "todo_app_sqlite_axum", "todo_app_sqlite_viz", diff --git a/examples/timer/Cargo.toml b/examples/timer/Cargo.toml new file mode 100644 index 000000000..ecb9aa1a4 --- /dev/null +++ b/examples/timer/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "timer" +version = "0.1.0" +edition = "2021" + +[profile.release] +codegen-units = 1 +lto = true + +[dependencies] +leptos = { path = "../../leptos" } +console_log = "1" +log = "0.4" +console_error_panic_hook = "0.1.7" +wasm-bindgen = "0.2" + +[dependencies.web-sys] +version = "0.3" +features = [ + "Window", +] + +[dev-dependencies] +wasm-bindgen-test = "0.3.0" diff --git a/examples/timer/Makefile.toml b/examples/timer/Makefile.toml new file mode 100644 index 000000000..ab9175602 --- /dev/null +++ b/examples/timer/Makefile.toml @@ -0,0 +1,9 @@ +[tasks.build] +command = "cargo" +args = ["+nightly", "build-all-features"] +install_crate = "cargo-all-features" + +[tasks.check] +command = "cargo" +args = ["+nightly", "check-all-features"] +install_crate = "cargo-all-features" diff --git a/examples/timer/README.md b/examples/timer/README.md new file mode 100644 index 000000000..5affd52f5 --- /dev/null +++ b/examples/timer/README.md @@ -0,0 +1,7 @@ +# Leptos Timer Example + +This example creates a simple timer based on `setInterval` in a client side rendered app with Rust and WASM. + +To run it, just issue the `trunk serve --open` command in the example root. This will build the app, run it, and open a new browser to serve it. + +> If you don't have `trunk` installed, [click here for install instructions.](https://trunkrs.dev/) diff --git a/examples/timer/index.html b/examples/timer/index.html new file mode 100644 index 000000000..75fa1f12a --- /dev/null +++ b/examples/timer/index.html @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/examples/timer/public/favicon.ico b/examples/timer/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2ba8527cb12f5f28f331b8d361eef560492d4c77 GIT binary patch literal 15406 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 k<4FtN!5 impl IntoView { + // count_a updates with a fixed interval of 1000 ms, whereas count_b has a dynamic + // update interval. + let (count_a, set_count_a) = create_signal(cx, 0_i32); + let (count_b, set_count_b) = create_signal(cx, 0_i32); + + let (interval, set_interval) = create_signal(cx, 1000); + + use_interval(cx, 1000, move || { + set_count_a.update(|c| *c = *c + 1); + }); + use_interval(cx, interval, move || { + set_count_b.update(|c| *c = *c + 1); + }); + + view! { cx, +
+
"Count A (fixed interval of 1000 ms)"
+
{count_a}
+
"Count B (dynamic interval, currently " {interval} " ms)"
+
{count_b}
+ () { + set_interval(value); + } + }/> +
+ } +} + +/// Hook to wrap the underlying `setInterval` call and make it reactive w.r.t. +/// possible changes of the timer interval. +pub fn use_interval(cx: Scope, interval_millis: T, f: F) +where + F: Fn() + Clone + 'static, + T: Into> + 'static, +{ + let interval_millis = interval_millis.into(); + create_effect(cx, move |prev_handle: Option| { + // effects get their previous return value as an argument + // each time the effect runs, it will return the interval handle + // so if we have a previous one, we cancel it + if let Some(prev_handle) = prev_handle { + prev_handle.clear(); + }; + + // here, we return the handle + set_interval_with_handle( + f.clone(), + // this is the only reactive access, so this effect will only + // re-run when the interval changes + Duration::from_millis(interval_millis.get()), + ) + .expect("could not create interval") + }); +} diff --git a/examples/timer/src/main.rs b/examples/timer/src/main.rs new file mode 100644 index 000000000..d1c553d31 --- /dev/null +++ b/examples/timer/src/main.rs @@ -0,0 +1,12 @@ +use leptos::*; +use timer::TimerDemo; + +pub fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + mount_to_body(|cx| { + view! { cx, + + } + }) +}