Dioxus 互动性

互动性

现在,我们的 HotDog 应用程序已经搭好了脚手架并设计好了样式,我们终于可以添加一些交互元素了。

封装状态

在进一步深入之前,我们先将应用程序分成两部分:TitleDogView 。这将有助于我们组织应用程序,并将 DogView 状态与 Title 状态分开。

rust 复制代码
#[component]
fn App() -> Element {
    rsx! {
        document::Stylesheet { href: CSS }
        Title {}
        DogView {}
    }
}

#[component]
fn Title() -> Element {
    rsx! {
        div { id: "title",
            h1 { "HotDog! 🌭" }
        }
    }
}

#[component]
fn DogView() -> Element {
    rsx! {
        div { id: "dogview",
            img { src: "https://images.dog.ceo/breeds/pitbull/dog-3981540_1280.jpg" }
        }
        div { id: "buttons",
            button { id: "skip", "skip" }
            button { id: "save", "save!" }
        }
    }
}

事件处理程序

DogView 组件中,我们希望为点击按钮附加一个动作。例如:跳过或保存当前的狗狗照片。我们可以使用 EventHandler 来监听点击事件。

事件处理程序与常规属性类似,但它们的名称通常以 on 开头,并接受闭包作为值。每当触发相应事件时,闭包就会被调用。监听器会接收事件对象中的事件信息。

我们将添加一些闭包,然后将它们传递给跳过和保存按钮的 onclick 属性:

rust 复制代码
#[component]
fn DogView() -> Element {
    let skip = move |evt| {};
    let save = move |evt| {};

    rsx! {
        // ...
        div { id: "buttons",
            button { onclick: skip, id: "skip",  "skip" }
            button { onclick: save, id: "save",  "save!" }
        }
    }
}

有关事件处理程序的更多信息,请参阅事件处理程序参考资料

使用use_hook 的状态

到目前为止,我们的组件还没有内部状态。对于 DogView,我们希望在用户点击跳过或保存时更改当前显示的狗狗照片。

为了在组件中存储状态,Dioxus 提供了 use_hook 函数。这使得裸 Rust 函数可以存储和加载状态,而无需使用额外的结构体。

在组件中调用时,use_hook 函数将返回一个原始存储值的 .clone()

rust 复制代码
#[component]
fn DogView() -> Element {
    let img_src = use_hook(|| "https://images.dog.ceo/breeds/pitbull/dog-3981540_1280.jpg");

    // ..

    rsx! {
        div { id: "dogview",
            img { src: "{img_src}" }
        }
        // ..
    }
}

Dioxus 钩子与 React 的钩子非常相似,需要遵循一些简单的规则才能正常运行。

信号和使用信号

虽然 use_hook 可以存储任何实现了 Clone 的值,但您经常需要一种更强大的状态管理方式。Dioxus 内置了信号。

信号 是普通 Rust 值的封装类型,它可以跟踪读写,让你的应用程序栩栩如生。你可以在信号中封装任何 Rust 值。信号可以通过 Signal::new() 手动创建,但我们强烈建议使用 use_signal 钩子。

手动创建信号需要记住在信号上调用 .manually_drop(),而 use_signal 会自动为你清理信号。

每当信号的值发生变化时,其包含的 "反应范围 "就会被 "标记为脏 "并重新运行。默认情况下,Dioxus 组件都是反应式作用域,因此只要信号值发生变化,就会重新渲染。

信号是 Dioxus 的核心 ,需要时间来掌握。我们建议您在开发第一个大型应用程序之前深入阅读状态管理指南

全局状态与上下文

虽然钩子能很好地管理组件的本地状态,但有时您也需要管理整个应用程序的状态。

Dioxus 提供了两种机制:上下文(Context )和全局信号(GlobalSignal)。

通过 Context API,父组件可以与子组件共享状态,而无需明确声明额外的属性字段。大型应用程序和程序库可利用这种机制在整个应用程序中共享状态,而无需修改组件签名。

要 "provide "上下文,只需使用实现 Clone 的结构调用 use_context_provider()。要读取子代中的上下文,请调用 use_context()

rust 复制代码
// Create a new wrapper type
#[derive(Clone)]
struct TitleState(String);

fn App() -> Element {
    // Provide that type as a Context
    use_context_provider(|| TitleState("HotDog".to_string()));
    rsx! {
        Title {}
    }
}

fn Title() -> Element {
    // Consume that type as a Context
    let title = use_context::<TitleState>();
    rsx! {
        h1 { "{title.0}" }
    }
}

您可以将 use_signalContext 结合起来,为应用程序提供反应状态:

rust 复制代码
#[derive(Clone, Copy)]
struct MusicPlayer {
    song: Signal<String>,
}

fn use_music_player_provider() {
    let song = use_signal(|| "Drift Away".to_string());
    use_context_provider(|| MusicPlayer { song });
}

通过 use_contextconsume_context,您可以轻松地修改该状态:

rust 复制代码
#[component]
fn Player() -> Element {
    rsx! {
        button {
            onclick: move |_| consume_context::<MusicPlayer>().song.set("Vienna".to_string()),
            "Shuffle"
        }
    }
}

当数值发生变化时,读取歌曲信号的任何组件都会自动重新渲染。

全局信号

有时您需要一个简单的全局值。这时,全局信号(GlobalSignal )就能帮上忙。GlobalSignals 是上下文系统和信号的组合,不需要额外的结构或设置。

只需在应用程序中声明一个 GlobalSignal 即可:

rust 复制代码
static SONG: GlobalSignal<String> = Signal::global(|| "Drift Away".to_string());

然后在任何地方读写:

rust 复制代码
#[component]
fn Player() -> Element {
    rsx! {
        h3 { "Now playing {SONG}" }
        button {
            onclick: move |_| *SONG.write() = "Vienna".to_string(),
            "Shuffle"
        }
    }
}

📣 GlobalSignals 只对一个应用程序具有全局性,而不是整个程序。在服务器上,每个应用程序都有自己的 GlobalSignal

对于 HotDog 来说,我们不需要 GlobalSignal 或 Context,但重要的是要知道您可以使用这些功能。

相关推荐
TunerT_TQ2 小时前
Valhalla 静态工程审阅 #008|RisingWave 源码证据驱动评测【大厂开源基础设施特辑】
rust·开源·github·实时数据处理·流式数据库·apacheflink·streamingsql
TunerT_TQ6 小时前
Valhalla 静态工程审阅 #007|AgentENV 源码证据驱动评测【大厂开源基础设施特辑】
rust·开源·go·github·sandbox·分布式系统·ai基础设施
NutShell Wang7 小时前
Vite 8.1 深度拆解:Rolldown 统一打包器如何终结前端构建的「双引擎时代」
前端·rust·开源·vite·开发者工具·vibe coding
寒水馨8 小时前
macOS下载、安装uv-0.12.0(附安装包uv-aarch64-apple-darwin.tar.gz)
python·macos·rust·项目管理·包管理器·astral·pip替代
寒水馨9 小时前
macOS下载、安装ripgrep-15.2.0(附安装包ripgrep-15.2.0-aarch64-apple-darwin.tar.gz)
macos·正则表达式·rust·ripgrep·rg·命令行搜索·代码搜索
SomeB1oody1 天前
【RustyML入门】1.0. 快速上手
开发语言·后端·机器学习·rust·教程
骊城英雄1 天前
Rust从入门到精通-trait
人工智能·算法·rust
花褪残红青杏小1 天前
Rust图像处理第23节-图像加噪:给每个像素单独掷一次骰子
rust·webassembly·图形学
小杍随笔1 天前
2025年Rust GUI框架实战万字避坑指南
开发语言·后端·rust
明月_清风2 天前
全栈工程师必会技术栈:从入门到架构的完整成长地图 🗺️
前端·后端·全栈