プロパティ
プロパティはしばしば "Props" と略されます。
プロパティは、基本的に Yew が監視できるコンポーネントの引数です。
型は、コンポーネントのプロパティとして使用される前に、`Properties` トレイトを実装する必要があります。
リアクティビティ
Yew は、再レンダリング中に仮想 DOM を調整する際に、プロパティが変更されたかどうかをチェックし、ネストされたコンポーネントを再レンダリングする必要があるかどうかを判断します。このように、Yew は非常に反応性の高いフレームワークとみなすことができます。親からの変更は常に下方向に伝播され、ビューはプロパティ/状態からのデータと同期しなくなることはありません。
まだチュートリアルを完了していない場合は、試してみて、このリアクティビティを自分でテストしてみてください。
derive マクロ
Yew は、構造体で `Properties` トレイトを簡単に実装するための derive マクロを提供しています。
`Properties` を derive する型は、Yew がデータ比較を行うことができるように、`PartialEq` も実装する必要があります。
use yew::Properties;
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
関数コンポーネントでの使用
属性 `#[function_component]` を使用すると、オプションで関数引数で Props を受け取ることができます。それらを供給するには、`html!` マクロの属性を介して割り当てられます。
- Props あり
- Props なし
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! { <>{"Am I loading? - "}{props.is_loading.clone()}</> }
}
// Then supply the prop
#[function_component]
fn App() -> Html {
html! {<HelloWorld is_loading={true} />}
}
use yew::{function_component, html, Html};
#[function_component]
fn HelloWorld() -> Html {
html! { "Hello world" }
}
// No props to supply
#[function_component]
fn App() -> Html {
html! {<HelloWorld />}
}
derive マクロのフィールド属性
`Properties` を derive する場合、デフォルトではすべてのフィールドが必要です。次の属性を使用すると、親が設定していない場合に使用されるデフォルト値をプロパティに指定できます。
属性は Rustdoc によって生成されたドキュメントには表示されません。プロパティのドキュメント文字列には、プロパティがオプションかどうか、および特別なデフォルト値があるかどうかを記載する必要があります。
- #[prop_or_default]
- #[prop_or(値)]
- #[prop_or_else(関数)]
`Default` トレイトを使用して、プロパティ値をフィールドの型のデフォルト値で初期化します。
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or_default]
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
if props.is_loading.clone() {
html! { "Loading" }
} else {
html! { "Hello world" }
}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld is_loading={true} />}
}
プロパティ値を初期化するには、`値` を使用します。 `値` は、フィールドの型を返す任意の式にすることができます。たとえば、ブール値のプロパティをデフォルトで `true` にするには、属性 `#[prop_or(true)]` を使用します。式は、プロパティが構築され、明示的な値が指定されていない場合に評価されます。
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or("Bob".to_string())]
pub name: String,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld name={"Sam".to_string()} />}
}
プロパティ値を初期化するには、`関数` を呼び出します。 `関数` は、`FnMut() -> T` というシグネチャを持つ必要があります。ここで、`T` はフィールドの型です。関数は、その属性に明示的な値が指定されていない場合に呼び出されます。
use yew::{function_component, html, Html, Properties};
fn create_default_name() -> String {
"Bob".to_string()
}
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or_else(create_default_name)]
pub name: String,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld name={"Sam".to_string()} />}
}
プロパティを使用する場合のメモリ/速度のオーバーヘッド
内部的には、プロパティは参照カウントされます。つまり、プロパティのコンポーネントツリーには共有ポインタのみが渡されます。これにより、プロパティ全体をクローンするコストを削減できます。これはコストがかかる場合があります。
文字列やその他の類似の型として定義する代わりに、属性値のカスタム型である `AttrValue` を使用します。
Props マクロ
`yew::props!` マクロを使用すると、`html!` マクロと同じ方法でプロパティを構築できます。
マクロは、属性または基本式(`Foo { ..base }`)を使用できないことを除いて、構造体式と同じ構文を使用します。型パスは、プロパティを直接指す(`path::to::Props`)か、コンポーネントの関連プロパティ(`MyComp::Properties`)を指すことができます。
use yew::{function_component, html, Html, Properties, props, virtual_dom::AttrValue};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or(AttrValue::from("Bob"))]
pub name: AttrValue,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
#[function_component]
fn App() -> Html {
let pre_made_props = props! {
Props {} // Notice we did not need to specify name prop
};
html! {<HelloWorld ..pre_made_props />}
}
評価順序
次の例に示すように、Props は指定された順序で評価されます。
#[derive(yew::Properties, PartialEq)]
struct Props { first: usize, second: usize, last: usize }
fn main() {
let mut g = 1..=3;
let props = yew::props!(Props { first: g.next().unwrap(), second: g.next().unwrap(), last: g.next().unwrap() });
assert_eq!(props.first, 1);
assert_eq!(props.second, 2);
assert_eq!(props.last, 3);
}
アンチパターン
ほとんどすべての Rust 型をプロパティとして渡すことができますが、避けるべきアンチパターンがいくつかあります。これらには以下が含まれますが、これらに限定されません。
- `AttrValue` の代わりに `String` 型を使用する。
**なぜこれが悪いのか?** `String` のクローンはコストがかかる可能性があります。プロパティ値がフックやコールバックで使用される場合、クローンが必要になることがよくあります。 `AttrValue` は参照カウントされた文字列(`Rc<str>`)または `&'static str` のいずれかであるため、クローンを作成するのに非常に安価です。
**注記**: `AttrValue` は内部的には implicit-clone の `IString` です。詳細については、そのクレートを参照してください。 - 内部可変性を使用する。
**なぜこれが悪いのか?** 内部可変性(`RefCell`、`Mutex` など)は *一般的に* 避けるべきです。再レンダリングで問題が発生する可能性があります(Yew は状態がいつ変更されたかを知らないため)、手動でレンダリングを強制する必要がある場合があります。すべてのものと同様に、それには適切な場所があります。注意して使用してください。 - 教えてください。もっと早く知っておけばよかったと思うようなエッジケースに遭遇しましたか?お気軽に issue を作成するか、このドキュメントに修正の PR を送信してください。
yew-autoprops
yew-autoprops は、関数の引数からその場で Props 構造体を作成できる実験的なパッケージです。プロパティ構造体が再利用されない場合は役立つ可能性があります。