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. |
|
| [ssr_router](ssr_router) | [F] | Demonstrates server-side rendering with routing. |
|
||||||
| [suspense](suspense) | [F] | This is an example that demonstrates `<Suspense />` support. |
|
| [suspense](suspense) | [F] | This is an example that demonstrates `<Suspense />` support. |
|
||||||
| [timer](timer) | [S] | Demonstrates the use of the interval and timeout services. |
|
| [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/). |
|
| [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. |
|
| [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/). |
|
| [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 => {
|
Msg::StartTimeout => {
|
||||||
let handle = {
|
let handle = {
|
||||||
let link = ctx.link().clone();
|
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);
|
self.timeout = Some(handle);
|
||||||
@ -75,7 +75,7 @@ impl Component for App {
|
|||||||
Msg::StartInterval => {
|
Msg::StartInterval => {
|
||||||
let handle = {
|
let handle = {
|
||||||
let link = ctx.link().clone();
|
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);
|
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