Yew Hooks

repository·main·Indexed 41 days ago

https://github.com/jetli/yew-hooks

A comprehensive library of reusable hooks for the Yew web framework, version 0.6.4. Inspired by react-use and vueuse, it provides utilities for state management (e.g., use_counter, use_list), side-effects (e.g., use_async, use_websocket), lifecycle handling, animations, browser sensors, and UI interactions.

Tokens
17.6K
Snippets
34
Records
54
Agent score
50%

What's inside Yew Hooks

  1. Overview of Yew Hooks

    main
    Yew Hooks is a collection of hooks for the Yew framework, inspired by popular libraries like react-use, alibaba/hooks, and vueuse/vueuse. It provides a wide range of utilities for managing state, side-effects, lifecycles, animations, sensors, and UI interactions in Yew applications.
  2. Understand the `UseDropHandle` state

    main

    The UseDropHandle returned by use_drop and use_drop_with_options contains several UseStateHandle fields that allow you to react to the drop state in your component's render logic:

    • over: A UseStateHandle<bool> that is true when a drag operation is currently occurring over the drop zone, and false otherwise.
    • files: A UseStateHandle<Option<Vec<File>>> containing the list of web_sys::File objects from the most recent file drop.
    • text: A UseStateHandle<Option<String>> containing the text content from the most recent text drop or paste.
    • uri: A UseStateHandle<Option<String>> containing the URI string from the most recent URI drop.

    Note that these handles are updated whenever a successful drop or paste event occurs.

  3. Use the `use_async` hook for asynchronous operations

    main

    The use_async hook manages the state of an asynchronous task. It tracks whether the task is currently loading, provides the resulting data (if successful), and captures any error encountered. You can trigger the execution of the async task manually by calling .run() on the returned handle.

    use serde::{de::DeserializeOwned, Deserialize, Serialize};
    use yew::prelude::*;
    use yew_hooks::prelude::*;
    
    #[function_component(UseAsync)]
    pub fn async_demo() -> Html {
        // The async block passed to use_async defines the task
        let state = use_async(async move { fetch_repo("jetli/yew-hooks".to_string()).await });
    
        let onclick = {
            let state = state.clone();
            Callback::from(move |_| {
                // Trigger the task manually
                state.run();
            })
        };
    
        html! {
            <div>
                <button {onclick} disabled={state.loading}>{ "Start to load repo: jetli/yew-hooks" }</button>
                <p>
                    { 
                        if state.loading { 
                            html! { "Loading, wait a sec..." } 
                        } else { 
                            html! {} 
                        } 
                    }
                </p>
                {
                    if let Some(repo) = &state.data {
                        html! {
                            <>
                                <p>{ "Repo name: " }<b>{ &repo.name }</p>
                                <p>{ "Repo full name: " }<b>{ &repo.full_name }</p>
                                <p>{ "Repo description: " }<b>{ &repo.description }</p>
                                <p>{ "Owner name: " }<b>{ &repo.owner.login }</p>
                                <p>{ "Owner avatar: " }<b><br/><img alt="avatar" src={repo.owner.avatar_url.clone()} /></b/></p>
                            </>
                        }
                    } else {
                        html! {}
                    }
                }
                <p>
                    {
                        if let Some(error) = &state.error {
                            match error {
                                Error::DeserializeError => html! { "DeserializeError" },
                                Error::RequestError => html! { "RequestError" },
                            }
                        } else {
                            html! {}
                        }
                    }
                </p>
            </div>
        }
    }
  4. Use the `use_counter` hook for state management

    main

    The use_counter hook provides a simple way to manage a numeric counter state. It returns a handle that allows you to increment, decrement, set, or reset the value. The handle can be cloned and moved into callbacks.

    use yew::prelude::*;
    use yew_hooks::prelude::*;
    
    #[function_component(Counter)]
    fn counter() -> Html {
        let counter = use_counter(0);
    
        let onincrease = {
            let counter = counter.clone();
            Callback::from(move |_| counter.increase())
        };
        let ondecrease = {
            let counter = counter.clone();
            Callback::from(move |_| counter.decrease())
        };
        let onincreaseby = {
            let counter = counter.clone();
            Callback::from(move |_| counter.increase_by(10))
        };
        let ondecreaseby = {
            let counter = counter.clone();
            Callback::from(move |_| counter.decrease_by(10))
        };
        let onset = {
            let counter = counter.clone();
            Callback::from(move |_| counter.set(100))
        };
        let onreset = {
            let counter = counter.clone();
            Callback::from(move |_| counter.reset())
        };
    
        html! {
            <div>
                <button onclick={onincrease}>{ "Increase" }</button>
                <button onclick={ondecrease}>{ "Decrease" }</button>
                <button onclick={onincreaseby}>{ "Increase by 10" }</button>
                <button onclick={ondecreaseby}>{ "Decrease by 10" }</button>
                <button onclick={onset}>{ "Set to 100" }</button>
                <button onclick={onreset}>{ "Reset" }</button>
                <p>
                    <b>{ "Current value: " }</b>
                    { *counter }
                </p>
            </div>
        }
    }
  5. Use the use_counter hook

    main

    The use_counter hook tracks the state of a number. It provides methods to manipulate the value, such as increase() and decrease(). To use it, import yew_hooks::prelude::* and call use_counter(initial_value) within a function component.

    use yew_hooks::prelude::*;
    
    #[function_component(Counter)]
    fn counter() -> Html {
        let counter = use_counter(0);
    
        let onincrease = {
            let counter = counter.clone();
            Callback::from(move |_| counter.increase())
        };
        let ondecrease = {
            let counter = counter.clone();
            Callback::from(move |_| counter.decrease())
        };
    
        html! {
            <>
                <button onclick={onincrease}>{ "Increase" }</button>
                <button onclick={ondecrease}>{ "Decrease" }</button>
                <b>{ "Current value: " }</b>
                { *counter }
            </>
        }
    }
  6. Use the `use_websocket` hook for WebSocket connections

    main

    The use_websocket hook manages a WebSocket connection. It provides access to the connection's ready_state and a message stream. You can send messages using the .send() method. It is common to use use_effect_with to react to incoming messages from the ws.message field.

    use yew::prelude::*;
    use yew_hooks::prelude::*;
    
    #[function_component(UseWebSocket)]
    pub fn web_socket() -> Html {
        let history = use_list(vec![]);
        let ws = use_websocket("wss://echo.websocket.events/".to_string());
    
        let onclick = {
            let ws = ws.clone();
            let history = history.clone();
            Callback::from(move |_| {
                let message = "Hello, world!".to_string();
                ws.send(message.clone());
                history.push(format!("[send]: {}", message));
            })
        };
    
        // Reacting to incoming messages
        {
            let history = history.clone();
            let ws = ws.clone();
            use_effect_with(
                ws.message,
                move |message| {
                    if let Some(message) = &**message {
                        history.push(format!("[recv]: {}", message.clone()));
                    }
                    || ()
                },
            );
        }
    
        html! {
            <div>
                <p>
                    <button {onclick} disabled={*ws.ready_state != UseWebSocketReadyState::Open}>{ "Send" }</button>
                </p>
                <p>
                    <b>{ "Message history: " }</b>
                </p>
                {
                    for history.current().iter().map(|message| {
                        html! {
                            <p>{ message }</p>
                        }
                    })
                }
            </div>
        }
    }
  7. Use the `use_theme` hook for dark/light mode

    main

    The use_theme hook manages user theme preferences (dark or light mode). It persists the choice to localStorage using a provided key. If no preference is stored, it defaults to the system's prefers-color-scheme. It provides methods like .toggle() and .is_dark() to manage and check the current theme.

    use yew::prelude::*;
    use yew_hooks::prelude::*;
    
    #[function_component(ThemeDemo)]
    fn theme_demo() -> Html {
        // Use a storage key for persistence across reloads/tabs
        let theme = use_theme("example_theme".to_string());
    
        let onclick = {
            let theme = theme.clone();
            Callback::from(move |_| theme.toggle())
        };
    
        html! {
            <>
                <button {onclick}>{ if theme.is_dark() { "Switch to light" } else { "Switch to dark" } }</button>
                <p>{ format!("Active theme: {}", *theme) }</p>
            </>
        }
    }
  8. Available Sensor hooks

    main

    Hooks to track browser and element properties:

    • use_window_size: tracks Window dimensions.
    • 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 browser's location value.
    • use_hash: tracks browser's location hash value.
    • use_search_param: tracks browser's location search param value.
    • use_size: tracks an HTML element's dimensions using the ResizeObserver API.
    • use_measure: tracks an HTML element's dimensions using the ResizeObserver API.
    • use_geolocation: tracks user's geographic location.
    • use_swipe: detects swipe based on TouchEvent.
    • use_long_press: detects when a user presses and holds an element for a specified duration.
    • use_visible: checks if an element is visible.
    • use_hovered: checks if an element is hovered.
    • use_permission: tracks browser's permission changes using the Permissions API.
    • use_idle: tracks whether the user is idle (not interacting with the page).
    • use_start_typing: triggers when user starts typing without an editable element focused.
    • use_media_query: tracks if a CSS media query matches (e.g. screen size, dark mode).