Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suspense example with struct component #3375

Merged
merged 2 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions examples/suspense/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ html, body {
.layout {
height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
display: grid;
justify-items: center;
align-items: center;
grid-template-columns: 1fr 1fr;
}

.content {
Expand Down Expand Up @@ -69,3 +69,7 @@ button {

color: rgb(100, 100, 100);
}

h2 {
text-align: center;
}
29 changes: 21 additions & 8 deletions examples/suspense/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
use web_sys::HtmlTextAreaElement;
use yew::prelude::*;

mod struct_consumer;
mod use_sleep;

use use_sleep::use_sleep;
pub use use_sleep::use_sleep;

#[derive(Debug, PartialEq, Properties)]
struct PleaseWaitProps {
from: &'static str,
}

#[function_component(PleaseWait)]
fn please_wait() -> Html {
html! {<div class="content-area">{"Please wait 5 Seconds..."}</div>}
fn please_wait(props: &PleaseWaitProps) -> Html {
html! {<div class="content-area">{"Please wait 5 Seconds for "}{props.from}{" component to load..."}</div>}
}

#[function_component(AppContent)]
Expand All @@ -20,7 +26,7 @@ fn app_content() -> HtmlResult {
let value = value.clone();

Callback::from(move |e: InputEvent| {
let input: HtmlTextAreaElement = e.target_unchecked_into();
let input: HtmlTextAreaElement = e.target_unchecked_into::<HtmlTextAreaElement>();

value.set(input.value());
})
Expand All @@ -41,14 +47,21 @@ fn app_content() -> HtmlResult {

#[function_component(App)]
fn app() -> Html {
let fallback = html! {<PleaseWait />};
let fallback_fn = html! {<PleaseWait from="function" />};
let fallback_struct = html! {<PleaseWait from="struct" />};

html! {
<div class="layout">
<div class="content">
<h1>{"Yew Suspense Demo"}</h1>
<Suspense fallback={fallback}>
<AppContent />
<h2>{" Yew Suspense Demo -- function component consumer"}</h2>
<Suspense fallback={fallback_fn}>
<AppContent />
</Suspense>
</div>
<div class="content">
<h2>{"Yew Suspense Demo -- struct component consumer"}</h2>
<Suspense fallback={fallback_struct}>
<struct_consumer::AppContent />
</Suspense>
</div>
</div>
Expand Down
70 changes: 70 additions & 0 deletions examples/suspense/src/struct_consumer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use web_sys::HtmlTextAreaElement;
use yew::prelude::*;

use crate::use_sleep;

#[function_component]
pub fn WithSleep<Comp>() -> HtmlResult
where
Comp: BaseComponent<Properties = AppContentProps>,
{
let sleep = use_sleep()?;
let sleep = Callback::from(move |_| sleep());
Ok(yew::virtual_dom::VChild::<Comp>::new(AppContentProps { resleep: sleep }, None).into())
}

#[derive(Debug, PartialEq, Properties)]
pub struct AppContentProps {
pub resleep: Callback<()>,
}

pub type AppContent = WithSleep<BaseAppContent>;

pub enum Msg {
ValueUpdate(String),
TakeABreak,
}

pub struct BaseAppContent {
value: String,
}

impl Component for BaseAppContent {
type Message = Msg;
type Properties = AppContentProps;

fn create(_ctx: &Context<Self>) -> Self {
Self {
value: "I am writing a long story...".to_string(),
}
}

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::ValueUpdate(v) => {
self.value = v;
}
Msg::TakeABreak => {
ctx.props().resleep.emit(());
}
};
true
}

fn view(&self, ctx: &Context<Self>) -> Html {
let oninput = ctx.link().callback(|e: InputEvent| {
let input: HtmlTextAreaElement = e.target_unchecked_into::<HtmlTextAreaElement>();
Msg::ValueUpdate(input.value())
});
let on_take_a_break = ctx.link().callback(|_| Msg::TakeABreak);
html! {
<div class="content-area">
<textarea value={self.value.clone()} {oninput}></textarea>
<div class="action-area">
<button onclick={on_take_a_break}>{"Take a break!"}</button>
<div class="hint">{"You can take a break at anytime"}<br />{"and your work will be preserved."}</div>
</div>
</div>
}
}
}
42 changes: 4 additions & 38 deletions website/docs/concepts/suspense.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,45 +136,11 @@ fn app() -> Html {
### Use Suspense in Struct Components

It's not possible to suspend a struct component directly. However, you
can use a function component as a [HOC](../advanced-topics/struct-components/hoc) to
achieve suspense-based data fetching.
can use a function component as a [Higher Order Component](../advanced-topics/struct-components/hoc)
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>;
```
The [suspense example in the Yew repository](https://github.com/yewstack/yew/tree/master/examples/suspense/src/struct_consumer.rs)
demonstrates how to use.

## Relevant examples

Expand Down
Loading