我有一个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;
},
}
});
}
}
}
以及生成的错误
`self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
...is used here...rustcE0759
我如何发出这个api请求并使用响应来更新self?
编辑:为了参考,这是我想要的功能的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>}
}
}
}
在创建异步块时,其中的任何内容都可能被抛出到另一个线程,或者可能在2年后被调用。这就是为什么铁锈不能让你把裁判移到它们里面,而不是静态的。
spawn_local()
函数具体状态为:
future必须是"static",因为它将被安排在后台运行,并且不能包含任何堆栈引用。
所以你不能使用对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
}
}