diff --git a/examples/README.md b/examples/README.md index f2ce09e1d..e9735ebd1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -59,6 +59,7 @@ As an example, check out the TodoMVC example here: ` support. | | [timer](timer) | [S] | Demonstrates the use of the interval and timeout services. | +| [timer_functional](timer_functional) | [F] | Demonstrates the use of the interval and timeout services using function components | | [todomvc](todomvc) | [S] | Implementation of [TodoMVC](http://todomvc.com/). | | [two_apps](two_apps) | [S] | Runs two separate Yew apps which can communicate with each other. | | [web_worker_fib](web_worker_fib) | [S] | Calculate Fibonacci numbers in a web worker thread using [`gloo-worker`](https://docs.rs/gloo-worker/latest/gloo_worker/). | diff --git a/examples/timer/src/main.rs b/examples/timer/src/main.rs index 00d0df450..073c38d0c 100644 --- a/examples/timer/src/main.rs +++ b/examples/timer/src/main.rs @@ -60,7 +60,7 @@ impl Component for App { Msg::StartTimeout => { let handle = { let link = ctx.link().clone(); - Timeout::new(3, move || link.send_message(Msg::Done)) + Timeout::new(3000, move || link.send_message(Msg::Done)) }; self.timeout = Some(handle); @@ -75,7 +75,7 @@ impl Component for App { Msg::StartInterval => { let handle = { let link = ctx.link().clone(); - Interval::new(1, move || link.send_message(Msg::Tick)) + Interval::new(1000, move || link.send_message(Msg::Tick)) }; self.interval = Some(handle); diff --git a/examples/timer_functional/Cargo.toml b/examples/timer_functional/Cargo.toml new file mode 100644 index 000000000..d84b071e0 --- /dev/null +++ b/examples/timer_functional/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "timer_functional" +version = "0.1.0" +authors = ["Zahash "] +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +gloo = "0.8.0" +js-sys = "0.3.61" +yew = { path = "../../packages/yew", features = ["csr"] } diff --git a/examples/timer_functional/README.md b/examples/timer_functional/README.md new file mode 100644 index 000000000..78b605bbc --- /dev/null +++ b/examples/timer_functional/README.md @@ -0,0 +1,17 @@ +# Timer Example + +[![Demo](https://img.shields.io/website?label=demo&url=https%3A%2F%2Fexamples.yew.rs%2Ftimer_functional)](https://examples.yew.rs/timer_functional) + +This is a technical demonstration for how to use timeouts and intervals. + +## Concepts + +The example mainly demonstrates the use of [`gloo_timer`](https://docs.rs/gloo-timers/ ) but does so using only [`function components`](https://yew.rs/docs/concepts/function-components). + +## Running + +Run a debug version of this application: + +```bash +trunk serve --open +``` \ No newline at end of file diff --git a/examples/timer_functional/index.html b/examples/timer_functional/index.html new file mode 100644 index 000000000..b3942ccfc --- /dev/null +++ b/examples/timer_functional/index.html @@ -0,0 +1,14 @@ + + + + + + Yew • Timer + + + + + + + + diff --git a/examples/timer_functional/index.scss b/examples/timer_functional/index.scss new file mode 100644 index 000000000..d801a2620 --- /dev/null +++ b/examples/timer_functional/index.scss @@ -0,0 +1,29 @@ +$font-stack: Roboto, sans-serif; +$primary-color: #008f53; + +body { + font: 100% $font-stack; + color: white; + background-color: $primary-color; +} + +#buttons { + text-align: right; +} + +#wrapper { + overflow: hidden; + width: 100%; +} + +#time { + text-align: center; + font-size: 17vh; + padding: 15% 0; + float: left; +} + +#messages { + float: right; +} + diff --git a/examples/timer_functional/src/main.rs b/examples/timer_functional/src/main.rs new file mode 100644 index 000000000..30da755dd --- /dev/null +++ b/examples/timer_functional/src/main.rs @@ -0,0 +1,167 @@ +use std::rc::Rc; + +use gloo::timers::callback::{Interval, Timeout}; +use yew::prelude::*; + +fn get_current_time() -> String { + let date = js_sys::Date::new_0(); + String::from(date.to_locale_time_string("en-US")) +} + +enum TimerAction { + Add(&'static str), + Cancel, + SetInterval(Interval), + SetTimeout(Timeout), + TimeoutDone, +} + +#[derive(Clone, Debug)] +struct TimerState { + messages: Vec<&'static str>, + interval_handle: Option>, + timeout_handle: Option>, +} + +impl PartialEq for TimerState { + fn eq(&self, other: &Self) -> bool { + self.messages == other.messages + && self.interval_handle.is_some() == other.interval_handle.is_some() + } +} + +impl Reducible for TimerState { + type Action = TimerAction; + + fn reduce(self: Rc, action: TimerAction) -> Rc { + match action { + TimerAction::Add(message) => { + let mut messages = self.messages.clone(); + messages.push(message); + Rc::new(TimerState { + messages, + interval_handle: self.interval_handle.clone(), + timeout_handle: self.timeout_handle.clone(), + }) + } + TimerAction::SetInterval(t) => Rc::new(TimerState { + messages: vec!["Interval started!"], + interval_handle: Some(Rc::from(t)), + timeout_handle: self.timeout_handle.clone(), + }), + TimerAction::SetTimeout(t) => Rc::new(TimerState { + messages: vec!["Timer started!!"], + interval_handle: self.interval_handle.clone(), + timeout_handle: Some(Rc::from(t)), + }), + TimerAction::TimeoutDone => { + let mut messages = self.messages.clone(); + messages.push("Done!"); + Rc::new(TimerState { + messages, + interval_handle: self.interval_handle.clone(), + timeout_handle: None, + }) + } + TimerAction::Cancel => { + let mut messages = self.messages.clone(); + messages.push("Canceled!"); + Rc::new(TimerState { + messages, + interval_handle: None, + timeout_handle: None, + }) + } + } + } +} + +#[function_component(Clock)] +fn clock() -> Html { + let time = use_state(get_current_time); + + { + let time = time.clone(); + use_effect_with((), |_| { + Interval::new(1000, move || time.set(get_current_time())).forget(); + }); + } + html!( +
{ time.as_str() }
+ ) +} + +#[function_component] +fn App() -> Html { + let state = use_reducer(|| TimerState { + messages: Vec::new(), + interval_handle: None, + timeout_handle: None, + }); + + let mut key = 0; + let messages: Html = state + .messages + .iter() + .map(|message| { + key += 1; + html! {

{ message }

} + }) + .collect(); + + let has_job = state.interval_handle.is_some() || state.timeout_handle.is_some(); + + let on_add_timeout = { + let state = state.clone(); + + Callback::from(move |_: MouseEvent| { + let timeout_state = state.clone(); + let message_state = state.clone(); + let t = Timeout::new(3000, move || { + message_state.dispatch(TimerAction::TimeoutDone); + }); + + timeout_state.dispatch(TimerAction::SetTimeout(t)); + }) + }; + + let on_add_interval = { + let state = state.clone(); + + Callback::from(move |_: MouseEvent| { + let interval_state = state.clone(); + let message_state = state.clone(); + let i = Interval::new(1000, move || { + message_state.dispatch(TimerAction::Add("Tick..")); + }); + + interval_state.dispatch(TimerAction::SetInterval(i)); + }) + }; + + let on_cancel = { + Callback::from(move |_: MouseEvent| { + state.dispatch(TimerAction::Cancel); + }) + }; + + html!( + <> +
+ + + +
+
+ +
+ { messages } +
+
+ + ) +} + +fn main() { + yew::Renderer::::new().render(); +}