Dioxus框架中组件属性(Component Props)

84 阅读4分钟

组件属性(Props)

就像你可以向函数传递参数或向元素传递属性一样,你可以向组件传递属性(props),以自定义其行为!我们到目前为止看到的组件都没有接受任何属性——那么让我们编写一些接受属性的组件。

#[derive(Props)]

组件属性是一个用#[derive(PartialEq, Clone, Props)]注解的单个结构体。为了让组件接受属性,其参数的类型必须是YourPropsStruct

示例:

// 记住:拥有属性必须实现`PartialEq`!
#[derive(PartialEq, Props, Clone)]
struct LikesProps {
    score: i32,
}

fn Likes(props: LikesProps) -> Element {
    rsx! {
        div {
            "This post has "
            b { "{props.score}" }
            " likes"
        }
    }
}

然后,你可以像传递元素的属性一样向组件传递属性值:

pub fn App() -> Element {
    rsx! { Likes { score: 42 } }
}

属性选项

#[derive(Props)]宏有一些特性,可以让你自定义属性的行为。

可选属性

你可以通过为字段使用Option<…>类型来创建可选字段:

#[derive(PartialEq, Clone, Props)]
struct OptionalProps {
    title: String,
    subtitle: Option<String>,
}

fn Title(props: OptionalProps) -> Element {
    rsx! {
        h1 { "{props.title}: ", {props.subtitle.unwrap_or_else(|| "No subtitle provided".to_string())} }
    }
}

然后,你可以选择提供它们或不提供:

Title { title: "Some Title" }
Title { title: "Some Title", subtitle: "Some Subtitle" }
// 显式提供Option不会编译:
// Title {
//     title: "Some Title",
//     subtitle: None,
// },

显式必需的Option

如果你想显式要求一个Option,而不是一个可选属性,你可以用#[props(!optional)]来注解它:

#[derive(PartialEq, Clone, Props)]
struct ExplicitOptionProps {
    title: String,
    #[props(!optional)]
    subtitle: Option<String>,
}

fn ExplicitOption(props: ExplicitOptionProps) -> Element {
    rsx! {
        h1 { "{props.title}: ", {props.subtitle.unwrap_or_else(|| "No subtitle provided".to_string())} }
    }
}

然后,你必须显式传递Some("str")None

ExplicitOption { title: "Some Title", subtitle: None }
ExplicitOption { title: "Some Title", subtitle: Some("Some Title".to_string()) }
// 这不会编译:
// ExplicitOption {
//     title: "Some Title",
// },

默认属性

你可以使用#[props(default = 42)]使一个字段成为可选的,并指定其默认值:

#[derive(PartialEq, Props, Clone)]
struct DefaultProps {
    // 当没有提供时,默认为42
    #[props(default = 42)]
    number: i64,
}

fn DefaultComponent(props: DefaultProps) -> Element {
    rsx! { h1 { "{props.number}" } }
}

然后,类似于可选属性,你不必提供它:

DefaultComponent { number: 5 }
DefaultComponent {}

使用into自动转换

在Rust函数中,通常接受impl Into<SomeType>而不是仅仅SomeType,以支持更广泛的参数范围。如果你想在属性中使用类似的功能,你可以使用#[props(into)]。例如,你可以在一个String属性上添加它——并且&str也会被自动接受,因为它可以被转换为String

#[derive(PartialEq, Props, Clone)]
struct IntoProps {
    #[props(into)]
    string: String,
}

fn IntoComponent(props: IntoProps) -> Element {
    rsx! { h1 { "{props.string}" } }
}

然后,你可以这样使用它:

IntoComponent { string: "some &str" }

组件宏

到目前为止,我们见过的每个组件函数都有一个对应的ComponentProps结构体来传递属性。这相当冗长……如果有属性作为简单的函数参数,那不是很好吗?然后我们就不需要定义Props结构体了,而是可以直接使用whatever而不是props.whatever

component允许你这样做。而不是输入“完整”版本:

#[derive(Props, Clone, PartialEq)]
struct TitleCardProps {
    title: String,
}

fn TitleCard(props: TitleCardProps) -> Element {
    rsx!{
        h1 { "{props.title}" }
    }
}

……你可以定义一个接受属性作为参数的函数。然后,只需用#[component]注解它,宏就会为你将其转换为常规组件:

#[component]
fn TitleCard(title: String) -> Element {
    rsx!{
        h1 { "{title}" }
    }
}

虽然新的组件更短,更易读,但库作者不应该使用这个宏,因为你对属性文档的控制较少。

组件子元素

在某些情况下,你可能希望创建一个作为其他内容容器的组件,而不需要组件知道这些内容是什么。为了实现这一点,创建一个类型为Element的属性:

#[derive(PartialEq, Clone, Props)]
struct ClickableProps {
    href: String,
    body: Element,
}

fn Clickable(props: ClickableProps) -> Element {
    rsx! {
        a { href: "{props.href}", class: "fancy-button", {props.body} }
    }
}

然后,在渲染组件时,你可以传入rsx!{...}的输出:

rsx! {
    Clickable {
        href: "https://www.youtube.com/watch?v=C-M2hs3sXGo",
        body: rsx! {
            "How to " i { "not" } " be seen"
        }
    }
}

警告:虽然它可能编译,但不要在RSX中多次包含同一个Element。结果行为是未指定的。

children字段

而不是通过常规属性传递RSX,你可能希望像元素可以有子元素一样接受子元素。“魔法”的children属性让你可以实现这一点:

#[derive(PartialEq, Clone, Props)]
struct ClickableProps {
    href: String,
    children: Element,
}

fn Clickable(props: ClickableProps) -> Element {
    rsx! {
        a { href: "{props.href}", class: "fancy-button", {props.children} }
    }
}

这使得使用组件更加简单:只需将RSX放在{}括号内——不需要render调用或其他宏!

rsx! {
    Clickable { href: "https://www.youtube.com/watch?v=C-M2hs3sXGo",
        "How to "
        i { "not" }
        " be seen"
    }
}

img