yew/website/docs/concepts/suspense.mdx
Kaede Hoshikawa 485a1b8c4a
Function Components & Hooks V2 (#2401)
* Make a use_hook hook with the new Hook trait.

* Implement Lifetime.

* Rewrites function signature.

* Only apply lifetime if there're other lifetimes.

* Cleanup signature rewrite logic.

* Rewrite hook body.

* Port some built-in hooks.

* Finish porting all built-in hooks.

* Port tests.

* Fix tests.

* Migrate to macro-based hooks.

* Fix HookContext, add tests on non-possible locations.

* Fix stderr for trybuild.

* Add 1 more test case.

* Adjust doc location.

* Pretty print hook signature.

* Fix Items & std::ops::Fn*.

* Add use_memo.

* Optimise Implementation of hooks.

* Use Box to capture function value only.

* Detect whether needs boxing.

* Add args if boxing not needed.

* Enforce hook number.

* Deduplicate use_effect.

* Optimise Implementation.

* Update documentation.

* Fix website test. Strip BoxedHook implementation from it.

* Allow doc string.

* Workaround doc tests.

* Optimise codebase & documentation.

* Fix website test.

* Reduce implementation complexity.

* Destructor is no more.

* Documentation and macros.

* Reduce heap allocation and hook complexity.

* Remove Queue as well.

* Prefer Generics.

* Fix typo.

* Remove more allocations.

* Add comments.

* Remove outdated comment.

* Bare Function Pointer for better code size.
2022-01-28 11:51:37 +02:00

183 lines
4.4 KiB
Plaintext

---
title: "Suspense"
description: "Suspense for data fetching"
---
Suspense is a way to suspend component rendering whilst waiting a task
to complete and a fallback (placeholder) UI is shown in the meanwhile.
It can be used to fetch data from server, wait for tasks to be completed
by an agent, or perform other background asynchronous task.
Before suspense, data fetching usually happens after (Fetch-on-render) or before
component rendering (Fetch-then-render).
### Render-as-You-Fetch
Suspense enables a new approach that allows components to initiate data request
during the rendering process. When a component initiates a data request,
the rendering process will become suspended and a fallback UI will be
shown until the request is completed.
The recommended way to use suspense is with hooks.
```rust ,ignore
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>
}
}
```
In the above example, the `use_user` hook will suspend the component
rendering while user information is loading and a `Loading...` placeholder will
be shown until `user` is loaded.
To define a hook that suspends a component rendering, it needs to return
a `SuspensionResult<T>`. When the component needs to be suspended, the
hook should return a `Err(Suspension)` and users should unwrap it with
`?` in which it will be converted into `Html`.
```rust ,ignore
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)
},
}
}
```
# Complete Example
```rust
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>
}
}
```
### Use Suspense in Struct Components
It's not possible to suspend a struct component directly. However, you
can use a function component as a Higher-Order-Component to
achieve suspense-based data fetching.
```rust ,ignore
use yew::prelude::*;
#[function_component(WithUser)]
fn with_user<T>() -> HtmlResult
where T: BaseComponent
{
let user = use_user()?;
Ok(html! {<T {user} />})
}
#[derive(Debug, PartialEq, Properties)]
pub struct UserContentProps {
pub user: User,
}
pub struct BaseUserContent;
impl Component for BaseUserContent {
type Properties = UserContentProps;
type Message = ();
fn create(ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
let name = ctx.props().user.name;
html! {<div>{"Hello, "}{name}{"!"}</div>}
}
}
pub type UserContent = WithUser<BaseUserContent>;
```
## Relevant examples
- [Suspense](https://github.com/yewstack/yew/tree/master/examples/suspense)