我有一个 yew 结构组件,它应该向 api 发出 get 请求,然后呈现项目列表。我正在尝试在组件的 render 方法内执行请求,但遇到了生命周期问题,无法在 wasm_bindgen_future 中使用对 self 的引用。我必须使用 wasm_bindgen_future 才能执行异步 api 请求。这是代码(大致)
pub struct ViewLessonPlans {
lesson_plans: Vec<LessonPlan>,
loading_condition: ComponentLoadingStage
}
impl Component for ViewLessonPlans {
type Message = ();
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
Self {
lesson_plans: vec![],
loading_condition: ComponentLoadingStage::Loading
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
match self.loading_condition {
ComponentLoadingStage::Loading => {
html! { <h1>{"Lesson Plans Loading"}</h1>}
},
ComponentLoadingStage::Success => {
self.lesson_plans.iter().map(|lp| {
html! { <ViewLessonPlan lesson_plan={lp.clone()} /> }
}).collect::<Html>()
},
ComponentLoadingStage::Error => {
html! { <h1>{ "There was an error loading the lesson plans!" }</h1>}
},
}
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
if first_render {
wasm_bindgen_futures::spawn_local(async move {
match get_lesson_plans().await {
Ok(lesson_plans) => {
self.lesson_plans = lesson_plans.iter().map(|(_id, lp)| {
lp.clone()
}).collect();
self.loading_condition = ComponentLoadingStage::Success;
},
Err(_) => {
self.loading_condition = ComponentLoadingStage::Error;
},
}
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
和生成的错误
`self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
...is used here...rustcE0759
Run Code Online (Sandbox Code Playgroud)
我如何发出此 api 请求并使用响应来更新自身?
编辑:作为参考,这是我所需功能的 function_component 版本。令人讨厌的是,它在刷新时短暂显示错误情况,不知道为什么。我对 ComponentLoadingStage 进行了一些重构,以便成功变体可以仅包含 api 响应内容,以使事情变得更简单。
#[function_component(ViewLessonPlans)]
pub fn view_lesson_plans() -> Html {
// Setup the state
let state_init: UseStateHandle<ComponentLoadingStage<Vec<LessonPlan>>> =
use_state(|| ComponentLoadingStage::Loading);
let state = state_init.clone();
// Perform the API request
wasm_bindgen_futures::spawn_local(async move {
match get_lesson_plans().await {
Ok(lesson_plans) => {
state.set(ComponentLoadingStage::Success(
lesson_plans.iter().map(|(_id, lp)| lp.clone()).collect(),
));
}
Err(_) => {
state.set(ComponentLoadingStage::Error);
}
}
});
// Return the view
match (*state_init).clone() {
ComponentLoadingStage::Loading => {
html! { <h1>{"Lesson Plans Loading"}</h1>}
}
ComponentLoadingStage::Success(lesson_plans) => lesson_plans
.iter()
.map(|lp| {
html! { <ViewLessonPlan lesson_plan={lp.clone()} /> }
})
.collect::<Html>(),
ComponentLoadingStage::Error => {
html! { <h1>{ "There was an error loading the lesson plans!" }</h1>}
}
}
}
Run Code Online (Sandbox Code Playgroud)
创建异步块时,其中的任何内容都可能会被扔到另一个线程,或者可能在 2 年后被调用。这就是为什么 Rust 不允许你将引用移动到除静态引用之外的引用中。
该spawn_local()函数具体指出:
future 必须是“静态的”,因为它将被安排在后台运行并且不能包含任何堆栈引用。
所以你不能使用对 self 的可变引用,但我的朋友有办法!
Yew 知道,在 UI 环境中,您需要一种在未知点(例如单击按钮)向自己发送信息的方法。
Yew 允许您创建更新方法,它们接受消息并且您可以对这些消息做出反应。
因此,您需要做的是创建一个链接,将其移动到异步块,调用该get_lesson_plans()方法,然后使用该链接发送消息。然后,您将在 update 方法中收到结果,该方法可以访问对 self 的可变引用。
它看起来像这样:
impl Component for ViewLessonPlans {
type Message = Result<..., ...>; // plug return type of get_lesson_plans()
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
Self {
lesson_plans: vec![],
loading_condition: ComponentLoadingStage::Loading
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
match self.loading_condition {
ComponentLoadingStage::Loading => {
html! { <h1>{"Lesson Plans Loading"}</h1>}
},
ComponentLoadingStage::Success => {
self.lesson_plans.iter().map(|lp| {
html! { <ViewLessonPlan lesson_plan={lp.clone()} /> }
}).collect::<Html>()
},
ComponentLoadingStage::Error => {
html! { <h1>{ "There was an error loading the lesson plans!" }</h1>}
},
}
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
if first_render {
let link = ctx.link().clone();
wasm_bindgen_futures::spawn_local(async move {
let result = get_lesson_plans().await;
link.send_message(result);
});
}
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Ok(lesson_plans) => {
self.lesson_plans = lesson_plans.iter().map(|(_id, lp)| {
lp.clone()
}).collect();
self.loading_condition = ComponentLoadingStage::Success;
},
Err(_) => {
self.loading_condition = ComponentLoadingStage::Error;
},
}
true
}
}
Run Code Online (Sandbox Code Playgroud)