暫停
暫停是一種方式,當在等待任務完成時,暫停元件渲染,並同時顯示一個後備(佔位符)UI。
它可用於從伺服器擷取資料、等待任務由代理完成,或是執行其他背景非同步任務。
在暫停之前,資料擷取通常會在元件渲染之後(渲染後擷取)或之前(先擷取再渲染)發生。
邊渲染邊擷取
暫停啟用一種新方法,讓元件可以在渲染過程中發起資料請求。當元件發起資料請求時,渲染過程將會暫停,直到請求完成前會顯示一個後備 UI。
建議使用掛勾的方式來使用暫停。
use yew::prelude::*;
#[function_component(Content)]
fn content() -> HtmlResult {
let user = use_user()?;
Ok(html! {<div>{"Hello, "}{&user.name}</div>})
}
#[function_component(App)]
fn app() -> Html {
let fallback = html! {<div>{"Loading..."}</div>};
html! {
<Suspense {fallback}>
<Content />
</Suspense>
}
}
在上述範例中,use_user
掛勾會在使用者資訊載入時暫停元件渲染,並會在載入 user
之前顯示一個 Loading...
佔位符。
若要定義一個暫停元件運算的鉤子,需要傳回一個 SuspensionResult<T>
。如果必須暫停元件,則鉤子應回傳 Err(Suspension)
,且使用者應以 ?
解開,如此一來即會轉換為 Html
。
use yew::prelude::*;
use yew::suspense::{Suspension, SuspensionResult};
struct User {
name: String,
}
#[hook]
fn use_user() -> SuspensionResult<User> {
match load_user() {
// If a user is loaded, then we return it as Ok(user).
Some(m) => Ok(m),
None => {
// When user is still loading, then we create a `Suspension`
// and call `SuspensionHandle::resume` when data loading
// completes, the component will be re-rendered
// automatically.
let (s, handle) = Suspension::new();
on_load_user_complete(move || {handle.resume();});
Err(s)
},
}
}
注意關於實作暫停鉤子的說明
Suspension::new
傳回 2 個值:暫停內容本身和一個暫停控制代碼。後者負責發出信號,表示何時重新呈現暫停的元件,提供 2 種可互換的方法來執行此操作
- 呼叫其
resume
方法。 - 放棄控制代碼。
危險
必須儲存暫停控制代碼,直到更新元件為止,即使用新接收的資料;否則,暫停的元件將進入無限的重新運算迴圈,進而影響效能。在上述範例中,儲存暫停控制代碼的方式是將其移入封閉函式,並傳遞至 on_load_user_complete
。當假設的使用者將會載入時,封閉函式將會被呼叫,進而呼叫 handle.resume()
,並重新呈現與暫停內容相關的元件。
完整範例
use yew::prelude::*;
use yew::suspense::{Suspension, SuspensionResult};
#[derive(Debug)]
struct User {
name: String,
}
fn load_user() -> Option<User> {
todo!() // implementation omitted.
}
fn on_load_user_complete<F: FnOnce()>(_fn: F) {
todo!() // implementation omitted.
}
#[hook]
fn use_user() -> SuspensionResult<User> {
match load_user() {
// If a user is loaded, then we return it as Ok(user).
Some(m) => Ok(m),
None => {
// When user is still loading, then we create a `Suspension`
// and call `SuspensionHandle::resume` when data loading
// completes, the component will be re-rendered
// automatically.
let (s, handle) = Suspension::new();
on_load_user_complete(move || {handle.resume();});
Err(s)
},
}
}
#[function_component(Content)]
fn content() -> HtmlResult {
let user = use_user()?;
Ok(html! {<div>{"Hello, "}{&user.name}</div>})
}
#[function_component(App)]
fn app() -> Html {
let fallback = html! {<div>{"Loading..."}</div>};
html! {
<Suspense {fallback}>
<Content />
</Suspense>
}
}
在結構元件中使用暫停
無法直接暫停結構元件。不過,可以使用函式元件作為 高階元件
,以取得基於暫停的資料擷取功能。
Yew 儲存庫中的暫停範例展示如何使用。