我正在构建一个 Yew 漫画阅读器应用程序。主页是一个包含缩略图的书架,单击缩略图会将我带到呈现书籍页面的页面。
fn switch(routes: Route) -> Html {
match routes {
Route::Home => html! {
<>
<h1>{ "Books" }</h1>
<BookShelf />
</>
},
Route::BookContent { name } => html! {
<div>
<BookContent name={name} />
</div>
},
Route::NotFound => html! { <div>{ "You sure that book exist?" }</div> },
}
}
#[function_component]
fn App() -> Html {
html! {
<HashRouter>
<Switch<Route> render={switch} />
</HashRouter>
}
}
BookShelf
获取我拥有的书籍列表,单独获取缩略图以在两列中呈现。
#[styled_component]
fn BookShelf() -> Html {
let books = use_state(|| vec![]);
{
let books = books.clone();
use_effect_with((), move |_| {
let books = books.clone();
wasm_bindgen_futures::spawn_local(async move {
let url = format!("{}/books", BACKEND_URL);
let fetched_books: Vec<String> = Request::get(&url)
.send()
.await
.unwrap()
.json()
.await
.unwrap();
books.set(fetched_books);
});
})
}
html! {
<>
{
for books.chunks(2).map(|books| html! {
<div class={css!("display:flex;")}>
{
books.iter().map(|book| html! {
<div class={css!("flex:50%;")}>
<Book name={book.clone()}/>
</div>
}).collect::<Html>()
}
</div>
})
}
</>
}
}
Book
组件实际上使用use_effect_with()
获取缩略图。单击时,它还会将路线切换到 Route::BookContent
。
BookContent
与 BookShelf
类似,只是它在同一列中获取和渲染图像。
页面的当前行为是,当我在获取和加载所有缩略图之前单击缩略图时,它会转到
BookContent
页面,但它会等待所有挂起的缩略图 GET
请求完成,然后再获取图书内容。
如何在
Route::Home
组件消失后中止所有待处理的获取请求(也称为单击缩略图转到 Route::BookContent
),以便我可以立即开始获取图书内容?
我刚刚看到 Theo 的视频“你现在比以往任何时候都更需要 React Query”,其中提到你可以向
useQuery
发送信号并中止查询。我想这可能是我需要的,如果路线改变,提前返回回调。可能使用上下文和 use_location
/ use_route
的组合?
所以实际上我应该中止的 future 是
gloo_net::http::Request
而不是 wasm_bindgen_futures::spwan_local
,因为 wasm future 会立即调度它所渲染的内容,而正在等待的是 Request
。幸运的是,通过设置 gloo_net::http::Request
,AbortSignal
中有一个中止机制。
利用
use_effect_with()
在组件被销毁时调用其返回值进行清理这一事实,我们可以在 Request
组件超出范围时中止 Image
。
use gloo_net::http::Request;
use web_sys::AbortController;
#[derive(Properties, PartialEq)]
struct ImageProperties {
url: String,
}
#[function_component]
fn Image(props: &ImageProperties) -> Html {
let image = use_state(|| String::new());
{
let image = image.clone();
let url = props.url.clone();
let controller = AbortController::new().unwrap();
let signal = controller.signal();
use_effect_with((), move |_| {
let _ = wasm_bindgen_futures::spawn_local(async move {
let res = Request::get(&url)
.abort_signal(Some(&signal))
.send()
.await
.unwrap();
let type_ = res.headers().get("Content-Type").unwrap();
let bytes = res.binary().await.unwrap();
let data = b64.encode(bytes);
image.set(format!("data:{};base64,{}", type_, data));
});
move || {
controller.abort();
}
});
}
html! {
<img
src={(*image).clone()}
alt={"loading"}
style="height:auto;width:100%;object-fit:inherit"
/>
}
}
请注意,如果您使用
渲染图像html! {
html! {
<img src={url} alt={"loading"}/>
}
}
即使组件被销毁,由
GET
标签调度的 img
请求也不会被中止。