跳至主要內容
版本:0.21

暫停

暫停是一種方式,當在等待任務完成時,暫停元件渲染,並同時顯示一個後備(佔位符)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 種可互換的方法來執行此操作

  1. 呼叫其 resume 方法。
  2. 放棄控制代碼。
危險

必須儲存暫停控制代碼,直到更新元件為止,即使用新接收的資料;否則,暫停的元件將進入無限的重新運算迴圈,進而影響效能。在上述範例中,儲存暫停控制代碼的方式是將其移入封閉函式,並傳遞至 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 儲存庫中的暫停範例展示如何使用。

相關範例