yew_hooks/hooks/use_event.rs
1use std::borrow::Cow;
2
3use gloo::events::{EventListener, EventListenerOptions};
4use gloo::utils::window;
5use wasm_bindgen::JsValue;
6use yew::prelude::*;
7
8use super::use_latest;
9
10/// A hook that subscribes a callback to events.
11///
12/// # Example
13///
14/// ```rust
15/// # use yew::prelude::*;
16/// # use log::debug;
17/// #
18/// use yew_hooks::prelude::*;
19///
20/// #[function_component(UseEvent)]
21/// fn event() -> Html {
22/// let button = use_node_ref();
23///
24/// use_event(button.clone(), "click", move |_: MouseEvent| {
25/// debug!("Clicked!");
26/// });
27///
28/// html! {
29/// <>
30/// <button ref={button}>{ "Click me!" }</button>
31/// </>
32/// }
33/// }
34/// ```
35#[hook]
36pub fn use_event<T, F, E>(node: NodeRef, event_type: T, callback: F)
37where
38 T: Into<Cow<'static, str>>,
39 F: Fn(E) + 'static,
40 E: From<JsValue>,
41{
42 let callback = use_latest(callback);
43
44 use_effect_with((node, event_type.into()), move |(node, event_type)| {
45 let window = window();
46 let node = node.get();
47 // If we cannot get the wrapped `Node`, then we use `Window` as the default target of the event.
48 let target = node.as_deref().map_or(&*window, |t| t);
49
50 // We should only set passive event listeners for `touchstart` and `touchmove`.
51 // See here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners
52 let listener =
53 if event_type == "touchstart" || event_type == "touchmove" || event_type == "scroll" {
54 Some(EventListener::new(
55 target,
56 event_type.clone(),
57 move |event| {
58 (*callback.current())(JsValue::from(event).into());
59 },
60 ))
61 } else {
62 Some(EventListener::new_with_options(
63 target,
64 event_type.clone(),
65 EventListenerOptions::enable_prevent_default(),
66 move |event| {
67 (*callback.current())(JsValue::from(event).into());
68 },
69 ))
70 };
71
72 move || drop(listener)
73 });
74}
75
76/// A hook that subscribes a callback to events only for window.
77/// If you want to specify an event target, use [`use_event`].
78///
79/// # Example
80///
81/// ```rust
82/// # use yew::prelude::*;
83/// # use log::debug;
84/// #
85/// use yew_hooks::prelude::*;
86///
87/// #[function_component(UseEvent)]
88/// fn event() -> Html {
89/// use_event_with_window("keypress", move |e: KeyboardEvent| {
90/// debug!("{} is pressed!", e.key());
91/// });
92///
93/// html! {
94/// <>
95/// { "Press any key on your awesome keyboard!" }
96/// </>
97/// }
98/// }
99/// ```
100#[hook]
101pub fn use_event_with_window<T, F, E>(event_type: T, callback: F)
102where
103 T: Into<Cow<'static, str>>,
104 F: Fn(E) + 'static,
105 E: From<JsValue>,
106{
107 use_event(NodeRef::default(), event_type, callback);
108}