mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Add a timer example that uses function components. (#3128)
* Start a functional timer example. * Start on the actions. * wip * Clean up a bit. * More sensible times. * Wip * Add canceled message. * Add a distinction between Cancel and Clear. * Bring timer_function up to parity with the class based example. * Fix clippy warnings. * cargo fmt * Update README. * Update examples/timer_functional/src/main.rs Co-authored-by: Jedd Dryden <40693089+Jaffa-Cakes@users.noreply.github.com> * Add README.md * Fix wqREADME.md spacing. * Add key to message iterator. * Update to new use_effect_with signature. --------- Co-authored-by: Jedd Dryden <40693089+Jaffa-Cakes@users.noreply.github.com> Co-authored-by: = <=>
This commit is contained in:
parent
f59c744efb
commit
8549ebe361
@ -59,6 +59,7 @@ As an example, check out the TodoMVC example here: <https://examples.yew.rs/todo
|
||||
| [ssr_router](ssr_router) | [F] | Demonstrates server-side rendering with routing. |
|
||||
| [suspense](suspense) | [F] | This is an example that demonstrates `<Suspense />` 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/). |
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
11
examples/timer_functional/Cargo.toml
Normal file
11
examples/timer_functional/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "timer_functional"
|
||||
version = "0.1.0"
|
||||
authors = ["Zahash <zahash.z@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
gloo = "0.8.0"
|
||||
js-sys = "0.3.61"
|
||||
yew = { path = "../../packages/yew", features = ["csr"] }
|
||||
17
examples/timer_functional/README.md
Normal file
17
examples/timer_functional/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Timer Example
|
||||
|
||||
[](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
|
||||
```
|
||||
14
examples/timer_functional/index.html
Normal file
14
examples/timer_functional/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Yew • Timer</title>
|
||||
<link data-trunk rel="scss" href="index.scss"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&display=swap" rel="stylesheet">
|
||||
<link data-trunk rel="rust" />
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
|
||||
</html>
|
||||
29
examples/timer_functional/index.scss
Normal file
29
examples/timer_functional/index.scss
Normal file
@ -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;
|
||||
}
|
||||
|
||||
167
examples/timer_functional/src/main.rs
Normal file
167
examples/timer_functional/src/main.rs
Normal file
@ -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<Rc<Interval>>,
|
||||
timeout_handle: Option<Rc<Timeout>>,
|
||||
}
|
||||
|
||||
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<Self>, action: TimerAction) -> Rc<Self> {
|
||||
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!(
|
||||
<div id="time">{ time.as_str() }</div>
|
||||
)
|
||||
}
|
||||
|
||||
#[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! { <p key={ key }>{ message }</p> }
|
||||
})
|
||||
.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!(
|
||||
<>
|
||||
<div id="buttons">
|
||||
<button disabled={has_job} onclick={on_add_timeout}>{ "Start Timeout" }</button>
|
||||
<button disabled={has_job} onclick={on_add_interval}>{ "Start Interval" }</button>
|
||||
<button disabled={!has_job} onclick={on_cancel}>{ "Cancel"}</button>
|
||||
</div>
|
||||
<div id="wrapper">
|
||||
<Clock />
|
||||
<div id="messages">
|
||||
{ messages }
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
yew::Renderer::<App>::new().render();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user