#hook #yew #yew-web #react #web-framework

yew-hooks

受 streamich/react-use 和 alibaba/hooks 等 React Hook 库启发的 Yew 网页框架的 Hooks。

61 个版本

0.3.2 2024年5月26日
0.3.1 2024年3月5日
0.3.0 2023年10月3日
0.2.0 2022年11月25日
0.1.2 2021年12月25日

#95 in Web 编程

Download history 1420/week @ 2024-04-26 1061/week @ 2024-05-03 1184/week @ 2024-05-10 1019/week @ 2024-05-17 1102/week @ 2024-05-24 1240/week @ 2024-05-31 1256/week @ 2024-06-07 1475/week @ 2024-06-14 1403/week @ 2024-06-21 1216/week @ 2024-06-28 965/week @ 2024-07-05 1272/week @ 2024-07-12 1561/week @ 2024-07-19 1180/week @ 2024-07-26 1558/week @ 2024-08-02 1147/week @ 2024-08-09

5,722 每月下载量
用于 11 个 crates(10 个直接使用)

Apache-2.0/MIT

215KB
4K SLoC

Yew Hooks


Yewstreamich/react-usealibaba/hooksvueuse/vueuse 启发的 Yew 的 Hooks。

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 }
        </>
    }
}

Hooks

状态

  • use_toggle - 跟踪对应者的状态。
  • use_bool_toggle - 跟踪布尔值的状态。
  • use_counter - 跟踪数字的状态。
  • use_latest - 返回状态或 props 的最新不可变 ref。
  • use_mut_latest - 返回状态或 props 的最新可变 ref。
  • use_previous - 返回状态或 props 的上一个不可变 ref。
  • use_list - 跟踪列表的状态。
  • use_map - 跟踪哈希表的状态。
  • use_set - 跟踪哈希集的状态。
  • use_queue - 跟踪队列的状态。
  • use_raf_state - 创建只在 requestAnimationFrame 之后更新的 set 方法。
  • use_state_ptr_eq - 类似于 use_state_eq,但检查两个 Rc 的值是否指向相同的分配。
  • use_renders_count - 计算组件渲染次数。
  • use_default - 当状态为 None 时返回默认值。
  • use_debounce_state - 节流状态。
  • use_throttle_state - 节流状态。

副作用

  • use_async - 解析一个 async 未来,例如获取 REST API。
  • use_websocket - 与 WebSocket 通信。
  • use_title - 设置页面的标题。
  • use_favicon - 设置页面的 favicon。
  • use_local_storage - 管理在 localStorage 中的值。
  • use_session_storage - 管理在 sessionStorage 中的值。
  • use_before_unload - 当用户尝试重新加载或关闭页面时显示浏览器警告。
  • use_debounce - 防抖一个函数。
  • use_debounce_effect - 防抖一个效果。
  • use_throttle - 限制一个函数的执行频率。
  • use_throttle_effect - 限制一个效果的执行频率。
  • use_clipboard - 读取或写入剪贴板中的文本/字节。

生命周期

  • use_effect_once - 修改过的 use_effect 钩子,只运行一次。
  • use_effect_update - 只在更新时运行效果。
  • use_mount - 调用挂载回调。
  • use_unmount - 调用卸载回调。
  • use_is_first_mount - 检查当前渲染是否为第一次。
  • use_is_mounted - 跟踪组件是否已挂载。
  • use_event - 订阅事件。
  • use_logger - 在组件通过生命周期时在控制台记录。

动画

  • use_timeout - 安排一个超时来调用回调。
  • use_interval - 安排一个间隔来调用回调。
  • use_update - 返回一个回调,当被调用时重新渲染组件。
  • use_raf - 在每个 requestAnimationFrame 上重新渲染组件。

传感器

  • use_window_size - 跟踪窗口尺寸。
  • use_window_scroll - 跟踪窗口滚动位置。
  • use_scroll - 跟踪 HTML 元素的滚动位置。
  • use_scrolling - 跟踪 HTML 元素是否在滚动。
  • use_infinite_scroll - 元素的无限滚动。
  • use_location - 跟踪浏览器的位置值。
  • use_hash - 跟踪浏览器的位置哈希值。
  • use_search_param - 跟踪浏览器的位置搜索参数值。
  • use_size - 使用 ResizeObserver API 跟踪 HTML 元素的尺寸。
  • use_measure - 使用 ResizeObserver API 跟踪 HTML 元素的尺寸。
  • use_geolocation - 跟踪用户的地理位置。
  • use_swipe - 根据TouchEvent检测滑动。
  • use_visible - 检查一个元素是否可见。

UI

  • use_click_away - 当用户点击目标元素外部时触发回调。
  • use_drag - 跟踪文件、链接和复制粘贴拖动,与 use_drop 钩子一起使用。
  • use_drop - 跟踪文件、链接和复制粘贴的放置。
  • use_media - 播放视频或音频并公开其控件。

示例

use_counter 示例

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>
    }
}

use_async 示例

use serde::{de::DeserializeOwned, Deserialize, Serialize};
use yew::prelude::*;
use yew_hooks::prelude::*;

#[function_component(UseAsync)]
pub fn async_demo() -> Html {
    let state = use_async(async move { fetch_repo("jetli/yew-hooks".to_string()).await });

    let onclick = {
        let state = state.clone();
        Callback::from(move |_| {
            // You can trigger to run in callback or use_effect.
            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 }</b></p>
                            <p>{ "Repo full name: " }<b>{ &repo.full_name }</b></p>
                            <p>{ "Repo description: " }<b>{ &repo.description }</b></p>

                            <p>{ "Owner name: " }<b>{ &repo.owner.login }</b></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>
    }
}

async fn fetch_repo(repo: String) -> Result<Repo, Error> {
    fetch::<Repo>(format!("https://api.github.com/repos/{}", repo)).await
}

/// You can use reqwest or other crates to fetch your api.
async fn fetch<T>(url: String) -> Result<T, Error>
where
    T: DeserializeOwned,
{
    let response = reqwest::get(url).await;
    if let Ok(data) = response {
        if let Ok(repo) = data.json::<T>().await {
            Ok(repo)
        } else {
            Err(Error::DeserializeError)
        }
    } else {
        Err(Error::RequestError)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
struct User {
    id: i32,
    login: String,
    avatar_url: String,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
struct Repo {
    id: i32,
    name: String,
    full_name: String,
    description: String,
    owner: User,
}

// You can use thiserror to define your errors.
#[derive(Clone, Debug, PartialEq)]
enum Error {
    RequestError,
    DeserializeError,
    // etc.
}

use_websocket 示例

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));
        })
    };

    {
        let history = history.clone();
        let ws = ws.clone();
        use_effect_with_deps(
            move |message| {
                if let Some(message) = &**message {
                    history.push(format!("[recv]: {}", message.clone()));
                }
                || ()
            },
            ws.message,
        );
    }

    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>
    }
}

演示

查看实时演示

贡献

欢迎查看此存储库中的当前问题,看看目前需要做什么。

如果您看到有什么缺失或可以改进的地方,也欢迎您打开一个 PR 或新问题。

许可

Apache-2.0/MIT

依赖

~11–15MB
~261K SLoC