🙆

RustのフレームワークYewを使ってみる③

2023/09/23に公開

前回までのあらすじ

ファンクションコンポーネントを分けたり、プロパティを使って表示を変えてみたりしました。

app.rs
use yew::prelude::*;
use gloo_console::log;

#[derive(Properties, PartialEq)]
pub struct Props {
    #[prop_or(true)]
    pub is_loading: bool,
}

#[function_component]
fn Login(props: &Props) -> Html {
    log!("LOGIN was loaded");
    if props.is_loading {
        //trueのときの処理
        html! {
            <>
                <p>{"LOGIN"}</p>
                <p>{format!("is_loading: {}", props.is_loading)}</p>
            </>
        }
    } else {
        //falseのときの処理
        html! {
            <>
                <p>{"LOGOUT"}</p>
                <p>{format!("is_loading: {}", props.is_loading)}</p>
            </>
        }
    }
}

#[function_component]
pub fn App() -> Html {
    log!("App was loaded");
    html! {
        <main>
            <h1>{ "Hello World!!" }</h1>
            <Login />
            <span>{ "Rust with Yew test" }</span>
        </main>
    }
}

プログラムを分割する

app.rsにたくさんのプログラムを書いて行くと可読性・保守性が低下してしまいます。
プログラムの中で、function_componentとして分かれていますが、プログラムが増えていくと、読みにくくなっていきます。そこで、ファイルを分けてapp.rsにモジュールとして読み込ませようと思います。
srcフォルダの下にappという名前でフォルダを作成します。フォルダ命名には規則があり、app.rsの子モジュールとなるため、フォルダ名はappとしなければなりません。
作成したappフォルダの中にhello.rsとsubtittle.rsを作成します。

Yewproject/  
├── yewapp/
│   ├── dist
│   ├── src
│   │   ├── app
│   │   │   └── login.rs
│   │   ├── app.rs
│   │   └── main.rs
│   ├── target
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── Dockerfile
│   ├── index.html
│   └── index.scss
└── docker-compose.yml

app.rsのからLogin関数をlogin.rsに移します。

login.rs
use yew::prelude::*;
use gloo_console::log;

#[derive(Properties, PartialEq)]
pub struct Props {
    #[prop_or(true)]
    pub is_loading: bool,
}

#[function_component]
pub fn Login(props: &Props) -> Html {
    log!("LOGIN was loaded");
    if props.is_loading {
        //trueのときの処理
        html! {
            <>
                <p>{"LOGIN"}</p>
                <p>{format!("is_loading: {}", props.is_loading)}</p>
            </>
        }
    } else {
        //falseのときの処理
        html! {
            <>
                <p>{"LOGOUT"}</p>
                <p>{format!("is_loading: {}", props.is_loading)}</p>
            </>
        }
    }
}

Loginをapp.rsで使用するために、login.rsをモジュールとして読み込み、Login関数を使用できるようにプログラムを追加します。

app.rs
use yew::prelude::*;
use gloo_console::log;

mod login;

use login::Login;

#[function_component]
pub fn App() -> Html {
    log!("App was loaded");
    html! {
        <main>
            <h1>{ "Hello World!!" }</h1>
            <Login />
            <span>{ "Rust with Yew test" }</span>
        </main>
    }
}

app.rsがシンプルになったので、app.rsのプログラムををmain.rsにまとめてしまっても良いかもしれません。

main.rs
use yew::prelude::*;
use gloo_console::log;

mod login;

use login::Login;

#[function_component]
pub fn App() -> Html {
    log!("App was loaded");
    html! {
        <main>
            <h1>{ "Hello World!!" }</h1>
            <Login />
            <span>{ "Rust with Yew test" }</span>
        </main>
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}

この場合のフォルダ構成は、以下のようになります。

Yewproject/  
├── yewapp/
│   ├── dist
│   ├── src
│   │   ├── login.rs
│   │   └── main.rs
│   ├── target
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── Dockerfile
│   ├── index.html
│   └── index.scss
└── docker-compose.yml

コールバックを使ってみる

コールバックを使ってonclickイベントを処理できるようにしてみる。
https://yew.rs/ja/docs/concepts/function-components/callbacks#dom-events-and-callbacks
yew公式を参考にSubtittle関数のspanタグにブレース付きでonclickイベントを追加し、Callbackオブジェクトonclickを作成して、クリックされたときの動作を記述する。

app.rs
#[function_component]
pub fn App() -> Html {
    log!("App was loaded");
    // onclickが設定されたspanタグがクリックされたときの動作(コンソールにログを表示)
    let onclick = Callback::from(move |_| {
        log!("span-tag was clicked");
    });
    html! {
        <main>
            <h1>{ "Hello World!!" }</h1>
            <Login />
            <span {onclick}>{ "Rust with Yew test" }</span>
        </main>
    }
}

let onclick = Callback::from(move |_| {…});で、Callbackオブジェクトonclickを作成します。このコールバックは、クリックイベント発生時に呼び出される関数を指定します。Callback::fromメソッドは、クロージャを引数として受け取り、それをコールバックに変換します。
spanタグに対してクリックイベントを設定し、{onclick} のように属性として onclickコールバックを指定します。これにより、ユーザーがspanタグをクリックすると、指定されたクリックハンドラ関数が呼び出されます。
move |_| { log!("span-tag was clicked"); } は、クリックハンドラ関数です。この関数は無名クロージャとして定義されており、クリックされたときに呼び出され、コンソールに"span-tag was clicked"というメッセージを出力します。

Callbackオブジェクトを代入する変数名を任意に指定したい

今後、onclickイベントが増えた場合、Callbackオブジェクトを代入する変数名を任意に指定したいので、プログラムを少し書き換えてみる。
spanタグにonlickイベントに{click_span}を追加して、Callbackオブジェクトもclick_spanに合わせて書き換え。

app.rs
#[function_component]
pub fn App() -> Html {
    log!("App was loaded");
    // onclickが設定されたspanタグがクリックされたときの動作(コンソールにログを表示)
    let click_span = Callback::from(move |_| {
        log!("span-tag was clicked");
    });
    html! {
        <main>
            <h1>{ "Hello World!!" }</h1>
            <Login />
            <span onclick={click_span}>{ "Rust with Yew test" }</span>
        </main>
    }
}

Callbackオブジェクトの部分を|:MouseEvent|クロージャに置き換えて書くことも可能なようです。|:MouseEvent|クロージャをclick_spanに束縛して、クリックされたときの動作を記述した感じになります。

app.rs
    let click_span = |_:MouseEvent| {
        log!("span-tag was clicked");
    };

クリックされたことだけが必要なので、クリックイベントハンドラがクリックに関連する情報(たとえば、クリックの座標やボタンの情報など)を受け取る必要はなく、引数を使用しません。クロージャの引数を使用しない(無視する)にも関わらず、クロージャの引数に'_'を使用しているのは、コンパイラの警告を回避するためです。

次回

inuput要素をためしたい。

Discussion