You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Yew.rs中执行异步请求?代码报错求助

Yew.rs 异步GET请求与状态更新问题修复

问题说明

在Yew.rs项目开发App组件时,实现GET请求遇到以下问题:

  • rust-analyzer提示books.clone();存在错误:can't capture dynamic environment in an fn item use the || { ... } closure form instead
  • 尝试使用use_async实现类似React中useState+useEffect的异步请求更新状态逻辑,仍报错,不清楚如何将请求结果传入set()方法

错误代码分析与修复

原始use_effect实现的问题

第一个代码示例存在三个核心问题:

  1. 独立的async fn fetch_books()无法捕获外部的UseStateHandle,必须用**异步闭包(async move {})**才能捕获组件环境中的状态变量
  2. HTTP请求语法错误:Request::get需要调用.send().await发送请求,还要解析响应为目标类型
  3. use_effect的闭包不能直接返回Future,需用wasm_bindgen_futures::spawn_local执行异步任务

修复后的use_effect实现代码

use gloo_net::http::Request;
use yew::prelude::*;
use wasm_bindgen_futures::spawn_local;
use serde::Deserialize;

// 定义BookList结构体,匹配后端返回字段
#[derive(Clone, Debug, Deserialize)]
struct BookList {
    id: u32,
    title: String,
}

#[function_component(App)]
pub fn app() -> Html {
    let books = use_state(|| Vec::<BookList>::new());

    use_effect(move || {
        // 克隆状态句柄,避免闭包转移所有权导致外部变量不可用
        let books_clone = books.clone();
        
        // 用spawn_local执行异步请求任务
        spawn_local(async move {
            match Request::get("http://localhost:3000/books")
                .send()
                .await
                .and_then(|res| res.json().await)
            {
                Ok(fetched_books) => {
                    books_clone.set(fetched_books);
                }
                Err(e) => {
                    // 打印请求错误日志
                    log::error!("Failed to fetch books: {:?}", e);
                }
            }
        });

        // 返回空清理函数(无清理需求)
        || ()
    });

    html! {
        <div>
            {for books.iter().map(|book| html! { <p>{&book.title}</p> })}
        </div>
    }
}

原始use_async实现的问题

第二个代码示例的问题:

  1. use_async的闭包没有正确发起请求、处理响应和返回结果
  2. 错误地在组件顶层调用fetch_books.set(),应该监听use_async的状态变化(如Success状态)来更新books
  3. 没有触发use_async的执行,需要调用run()方法

修复后的use_async实现代码

use gloo_net::http::Request;
use yew::prelude::*;
use yew_hooks::use_async; // 需要在Cargo.toml中添加yew-hooks依赖
use serde::Deserialize;

#[derive(Clone, Debug, Deserialize)]
struct BookList {
    id: u32,
    title: String,
}

#[function_component(App)]
pub fn app() -> Html {
    let books = use_state(|| Vec::<BookList>::new());

    // 定义异步请求逻辑,返回Result类型
    let fetch_books = use_async(async {
        Request::get("http://localhost:3000/books")
            .send()
            .await?
            .json()
            .await
    });

    // 监听use_async状态变化,请求成功时更新books状态
    {
        let books = books.clone();
        use_effect_with_deps(move |fetch_result| {
            if let Some(Ok(fetched_books)) = fetch_result {
                books.set(fetched_books.clone());
            }
            || ()
        }, fetch_books.state.clone());
    }

    // 组件挂载时触发请求执行
    use_effect(move || {
        fetch_books.run();
        || ()
    });

    html! {
        <div>
            {match &fetch_books.state {
                Some(Ok(books_data)) => {
                    html! { for books_data.iter().map(|book| html! { <p>{&book.title}</p> })}
                }
                Some(Err(e)) => {
                    html! { <p>{"Failed to load books: "}{e.to_string()}</p> }
                }
                None => {
                    html! { <p>{"Loading..."}</p> }
                }
            }}
        </div>
    }
}

关键注意点

  • 捕获状态句柄时必须调用clone(),因为异步闭包会转移变量所有权
  • 异步任务需要用spawn_local执行(use_effect场景),或者通过use_async的run()触发
  • 必须为BookList实现serde::Deserialize trait,才能将JSON响应解析为结构体
  • 必须处理请求错误,避免运行时panic

内容的提问来源于stack exchange,提问作者Daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 18:50:24