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}