From 7ccfe08a0dca56d46b14da180ed6ed137fa47bfd Mon Sep 17 00:00:00 2001 From: Jet Li Date: Sat, 7 May 2022 17:13:17 +0800 Subject: [PATCH] add use_infinite_scroll (#12) --- README.md | 1 + crates/yew-hooks/Cargo.toml | 2 +- crates/yew-hooks/src/hooks/mod.rs | 2 + .../src/hooks/use_infinite_scroll.rs | 79 +++++++++++++++++++ crates/yew-hooks/src/hooks/use_scrolling.rs | 26 +++--- examples/yew-app/src/routes/home.rs | 3 +- examples/yew-app/src/routes/hooks/mod.rs | 2 + .../src/routes/hooks/use_infinite_scroll.rs | 39 +++++++++ examples/yew-app/src/routes/mod.rs | 3 + 9 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 crates/yew-hooks/src/hooks/use_infinite_scroll.rs create mode 100644 examples/yew-app/src/routes/hooks/use_infinite_scroll.rs diff --git a/README.md b/README.md index 2986d59..e8a9e04 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ fn counter() -> Html { - `use_window_scroll` - tracks Window scroll position. - `use_scroll` - tracks an HTML element's scroll position. - `use_scrolling` - tracks whether HTML element is scrolling. +- `use_infinite_scroll` - infinite scrolling of the element. - `use_location` - tracks brower's location value. - `use_hash` - tracks brower's location hash value. - `use_search_param` - tracks brower's location search param value. diff --git a/crates/yew-hooks/Cargo.toml b/crates/yew-hooks/Cargo.toml index 549a039..3fcaf50 100644 --- a/crates/yew-hooks/Cargo.toml +++ b/crates/yew-hooks/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yew-hooks" -version = "0.1.54" +version = "0.1.55" edition = "2018" authors = ["Jet Li "] categories = ["gui", "wasm", "web-programming"] diff --git a/crates/yew-hooks/src/hooks/mod.rs b/crates/yew-hooks/src/hooks/mod.rs index efe9e41..36d7ea5 100644 --- a/crates/yew-hooks/src/hooks/mod.rs +++ b/crates/yew-hooks/src/hooks/mod.rs @@ -15,6 +15,7 @@ mod use_event; mod use_favicon; mod use_geolocation; mod use_hash; +mod use_infinite_scroll; mod use_interval; mod use_is_first_mount; mod use_is_mounted; @@ -69,6 +70,7 @@ pub use use_event::*; pub use use_favicon::*; pub use use_geolocation::*; pub use use_hash::*; +pub use use_infinite_scroll::*; pub use use_interval::*; pub use use_is_first_mount::*; pub use use_is_mounted::*; diff --git a/crates/yew-hooks/src/hooks/use_infinite_scroll.rs b/crates/yew-hooks/src/hooks/use_infinite_scroll.rs new file mode 100644 index 0000000..b632b1e --- /dev/null +++ b/crates/yew-hooks/src/hooks/use_infinite_scroll.rs @@ -0,0 +1,79 @@ +use web_sys::Element; +use yew::prelude::*; + +use super::{use_debounce, use_event, use_latest}; + +/// A sensor hook that tracks infinite scrolling of the element. +/// +/// # Example +/// +/// ```rust +/// # use yew::prelude::*; +/// # +/// use yew_hooks::{use_infinite_scroll, use_list}; +/// +/// #[function_component(UseInfiniteScroll)] +/// fn infinite_scroll() -> Html { +/// let node = use_node_ref(); +/// let state = use_list(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +/// +/// { +/// let state = state.clone(); +/// use_infinite_scroll(node.clone(), move || { +/// let max = state.current().len() + 1; +/// let mut more = vec![max, max + 1, max + 2, max + 3, max + 4]; +/// state.append(&mut more); +/// }); +/// } +/// +/// html! { +///
+/// { +/// for state.current().iter().map(|element| { +/// html! {

{ element }

} +/// }) +/// } +///
+/// } +/// } +/// ``` +pub fn use_infinite_scroll(node: NodeRef, callback: Callback) +where + Callback: Fn() + 'static, +{ + let callback_ref = use_latest(callback); + let load_more = use_state_eq(|| false); + + { + let load_more = load_more.clone(); + use_effect_with_deps( + move |load_more| { + if **load_more { + let callback = &*callback_ref.current(); + callback(); + } + + || () + }, + load_more, + ); + } + + let debounce = { + let load_more = load_more.clone(); + use_debounce( + move || { + load_more.set(false); + }, + 150, + ) + }; + + use_event(node, "scroll", move |e: Event| { + let element: Element = e.target_unchecked_into(); + if element.scroll_height() - element.scroll_top() <= element.client_height() { + load_more.set(true); + debounce.run(); + } + }); +} diff --git a/crates/yew-hooks/src/hooks/use_scrolling.rs b/crates/yew-hooks/src/hooks/use_scrolling.rs index 34eed51..7cf197d 100644 --- a/crates/yew-hooks/src/hooks/use_scrolling.rs +++ b/crates/yew-hooks/src/hooks/use_scrolling.rs @@ -1,7 +1,6 @@ -use gloo::timers::callback::Timeout; use yew::prelude::*; -use super::{use_event, use_unmount}; +use super::{use_debounce, use_event}; /// A sensor hook that tracks whether HTML element is scrolling. /// @@ -26,24 +25,25 @@ use super::{use_event, use_unmount}; /// } /// ``` pub fn use_scrolling(node: NodeRef) -> bool { - let state = use_state(|| false); - let timer = use_mut_ref(|| None); + let state = use_state_eq(|| false); + + let debounce = { + let state = state.clone(); + use_debounce( + move || { + state.set(false); + }, + 150, + ) + }; { let state = state.clone(); - let timer = timer.clone(); use_event(node, "scroll", move |_: Event| { state.set(true); - let state = state.clone(); - *timer.borrow_mut() = Some(Timeout::new(150, move || { - state.set(false); - })); + debounce.run(); }); } - use_unmount(move || { - *timer.borrow_mut() = None; - }); - *state } diff --git a/examples/yew-app/src/routes/home.rs b/examples/yew-app/src/routes/home.rs index d14584d..95ba9f5 100644 --- a/examples/yew-app/src/routes/home.rs +++ b/examples/yew-app/src/routes/home.rs @@ -25,7 +25,7 @@ pub fn home() -> Html {
  • to={AppRoute::UseSet} classes="app-link">{ "use_set" }> { " - tracks state of a hash set." }
  • to={AppRoute::UseQueue} classes="app-link">{ "use_queue" }> { " - tracks state of a queue." }
  • to={AppRoute::UseRafState} classes="app-link">{ "use_raf_state" }> { " - creates set method which only updates after requestAnimationFrame." }
  • -
  • to={AppRoute::UseStatePtrEq} classes="app-link">{ "use_state_ptr_eq" }> { " - similar to use_state_eq, but checks if the two Rcs of values point to the same allocation." }
  • +
  • to={AppRoute::UseStatePtrEq} classes="app-link">{ "use_state_ptr_eq" }> { " - similar to use_state_eq, but checks two Rcs' pointers of allocation." }
  • to={AppRoute::UseRendersCount} classes="app-link">{ "use_renders_count" }> { " - counts component renders." }
  • to={AppRoute::UseDefault} classes="app-link">{ "use_default" }> { " - returns the default value when state is None." }
  • to={AppRoute::UseDebounceState} classes="app-link">{ "use_debounce_state" }> { " - debounces state." }
  • @@ -78,6 +78,7 @@ pub fn home() -> Html {
  • to={AppRoute::UseWindowScroll} classes="app-link" >{ "use_window_scroll" }> { " - tracks Window scroll position." }
  • to={AppRoute::UseScroll} classes="app-link" >{ "use_scroll" }> { " - tracks an HTML element's scroll position." }
  • to={AppRoute::UseScrolling} classes="app-link" >{ "use_scrolling" }> { " - tracks whether HTML element is scrolling." }
  • +
  • to={AppRoute::UseInfiniteScroll} classes="app-link" >{ "use_infinite_scroll" }> { " - infinite scrolling of the element." }
  • to={AppRoute::UseLocation} classes="app-link" >{ "use_location" }> { " - tracks brower's location value." }
  • to={AppRoute::UseHash} classes="app-link" >{ "use_hash" }> { " - tracks brower's location hash value." }
  • to={AppRoute::UseSearchParam} classes="app-link" >{ "use_search_param" }> { " - tracks brower's location search param value." }
  • diff --git a/examples/yew-app/src/routes/hooks/mod.rs b/examples/yew-app/src/routes/hooks/mod.rs index 2b90e7f..f11a856 100644 --- a/examples/yew-app/src/routes/hooks/mod.rs +++ b/examples/yew-app/src/routes/hooks/mod.rs @@ -16,6 +16,7 @@ mod use_event; mod use_favicon; mod use_geolocation; mod use_hash; +mod use_infinite_scroll; mod use_interval; mod use_is_first_mount; mod use_is_mounted; @@ -72,6 +73,7 @@ pub use use_event::*; pub use use_favicon::*; pub use use_geolocation::*; pub use use_hash::*; +pub use use_infinite_scroll::*; pub use use_interval::*; pub use use_is_first_mount::*; pub use use_is_mounted::*; diff --git a/examples/yew-app/src/routes/hooks/use_infinite_scroll.rs b/examples/yew-app/src/routes/hooks/use_infinite_scroll.rs new file mode 100644 index 0000000..3a0ea18 --- /dev/null +++ b/examples/yew-app/src/routes/hooks/use_infinite_scroll.rs @@ -0,0 +1,39 @@ +use yew::prelude::*; + +use yew_hooks::{use_infinite_scroll, use_list}; + +/// `use_infinite_scroll` demo +#[function_component(UseInfiniteScroll)] +pub fn infinite_scroll() -> Html { + let node = use_node_ref(); + let state = use_list(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + { + let state = state.clone(); + use_infinite_scroll(node.clone(), move || { + let max = state.current().len() + 1; + let mut more = vec![max, max + 1, max + 2, max + 3, max + 4]; + state.append(&mut more); + }); + } + + html! { +
    +
    +
    +
    +
    + { "Try to scroll in this area vertically." } + { + for state.current().iter().map(|element| { + html! {

    { element }

    } + }) + } + +
    +
    +
    +
    +
    + } +} diff --git a/examples/yew-app/src/routes/mod.rs b/examples/yew-app/src/routes/mod.rs index f4794fc..f759272 100644 --- a/examples/yew-app/src/routes/mod.rs +++ b/examples/yew-app/src/routes/mod.rs @@ -124,6 +124,8 @@ pub enum AppRoute { UseFavicon, #[at("/use_clipboard")] UseClipboard, + #[at("/use_infinite_scroll")] + UseInfiniteScroll, #[not_found] #[at("/page-not-found")] PageNotFound, @@ -191,6 +193,7 @@ pub fn switch(routes: &AppRoute) -> Html { AppRoute::UseThrottleEffect => html! { }, AppRoute::UseFavicon => html! { }, AppRoute::UseClipboard => html! { }, + AppRoute::UseInfiniteScroll => html! { }, AppRoute::PageNotFound => html! { }, } }