Dioxus 中的数据获取

76 阅读3分钟

获取数据

我们的 HotDog 应用程序具有一些基本的交互性,但还不能获取新的狗图像。在本章中,我们将使用异步从 API 接口获取数据。

添加依赖关系

Dioxus 没有提供任何用于获取数据的内置实用程序。但在本教程中,我们将从头开始实现数据获取。

首先,我们需要为应用程序添加两个新的依赖项:serdereqwest

  • Reqwest 提供了一个用于获取数据的 HTTP 客户端。
  • Serde 会让我们派生一个 JSON 反序列化器来解码响应。

在新的终端窗口中,使用 cargo add 将这些 crate 添加到应用程序中。

cargo add reqwest --features json
cargo add serde --features derive

定义响应类型

我们将使用神奇的 dog.ceo/dog-api 为 HotDog 抓取狗的图片。幸运的是,API 响应的反序列化非常简单。

让我们创建一个符合 API 格式的新 Rust 结构,并为其派生 Deserialize。

Dog API 文档概述了一个示例 API 响应:

{
    "message": "https://images.dog.ceo/breeds/leonberg/n02111129_974.jpg",
    "status": "success"
}

我们的 Rust 结构需要与这种格式相匹配,不过现在我们只包括 message 字段。

#[derive(serde::Deserialize)]
struct DogApi {
    message: String,
}

使用 reqwest 和 async

Dioxus 支持异步 Rust。我们只需将 onclick 处理程序转换为异步,然后在异步完成后设置 img_src

fetch-dog-d4cc800af947297d.gif

对代码的修改非常简单--只需添加 reqwest::get 调用,然后在 img_src 上调用 .set()设置结果。

#[component]
fn DogView() -> Element {
    let mut img_src = use_signal(|| "".to_string());

    let fetch_new = move |_| async move {
        let response = reqwest::get("https://dog.ceo/api/breeds/image/random")
            .await
            .unwrap()
            .json::<DogApi>()
            .await
            .unwrap();

        img_src.set(response.message);
    };

    // ..

    rsx! {
        div { id: "dogview",
            img { src: "{img_src}" }
        }
        div { id: "buttons",
            // ..
            button { onclick: fetch_new, id: "save", "save!" }
        }
    }
}

Dioxus 会 自动在异步闭包上调用 spawn。您也可以使用 spawn 执行异步工作,而无需异步闭包,只需在任何异步块上调用 spawn() 即可。

rsx! {
    button {
        onclick: move |_| {
            spawn(async move {
                // do some async work...
            });
        }
    }
}

传递给 spawn 的 future 不得包含对异步块外数据的潜在引用。复制的数据可以被异步块捕获,但所有其他数据都必须 move,通常是通过调用 .clone()

使用 use_resource 管理数据抓取

最终,使用裸异步调用可能会导致竞赛条件和奇怪的状态错误。例如,如果用户点击获取按钮的速度过快,就会并行发出两个请求。如果请求正在更新其他地方的数据,错误的请求可能会提前完成,从而导致竞赛条件。

在 Dioxus 中,资源是状态的一部分,其值取决于某些异步工作的完成情况。use_resource 钩子为资源对象提供了启动停止暂停修改异步状态的有用方法。

让我们将组件改为使用 use_resource

#[component]
fn DogView() -> Element {
    let mut img_src = use_resource(|| async move {
        reqwest::get("https://dog.ceo/api/breeds/image/random")
            .await
            .unwrap()
            .json::<DogApi>()
            .await
            .unwrap()
            .message
    });

    rsx! {
        div { id: "dogview",
            img { src: img_src.cloned().unwrap_or_default() }
        }
        div { id: "buttons",
            button { onclick: move |_| img_src.restart(), id: "skip", "skip" }
            button { onclick: move |_| img_src.restart(), id: "save", "save!" }
        }
    }
}

资源非常强大:可与 SuspenseStreaming HTML反应性等集成。

资源应用程序接口的细节现在还不是很重要,但您将在大型应用程序中频繁使用资源,因此最好阅读一下文档