Yew Hooks

crates.io Version crates.io Downloads docs.rs Docs Github actions CI status

Demos | Examples | Docs


Hooks for Yew, inspired by streamich/react-use, alibaba/hooks and vueuse/vueuse.

```rust 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

State

Side-effects

Lifecycles

Animations

Sensors

UI

Examples

use_counter demo

```rust 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 demo

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

[function_component(UseAsync)]

pub fn asyncdemo() -> Html { let state = useasync(async move { fetchrepo("jetli/yew-hooks".tostring()).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 { fetch::(format!("https://api.github.com/repos/{}", repo)).await }

/// You can use reqwest or other crates to fetch your api. async fn fetch(url: String) -> Result where T: DeserializeOwned, { let response = reqwest::get(url).await; if let Ok(data) = response { if let Ok(repo) = data.json::().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 demo

```rust use yew::prelude::; use yew_hooks::prelude::;

[function_component(UseWebSocket)]

pub fn websocket() -> Html { let history = uselist(vec![]); let ws = usewebsocket("wss://echo.websocket.events/".tostring());

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

} ```

Demo

Check out a live demo

Contribute

Feel free to take a look at the current issues in this repo for anything that currently needs to be worked on.

You are also welcome to open a PR or a new issue if you see something is missing or could be improved upon.

License

Apache-2.0/MIT