Skip to content

Commit

Permalink
Suspense example with struct component (#3375)
Browse files Browse the repository at this point in the history
* Add example for struct component context consumer

* Replace untested docs example with one from examples dir

Fixes #3351
  • Loading branch information
ranile authored Aug 14, 2023
1 parent 3e9e253 commit 05fc4cc
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 49 deletions.
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

0 comments on commit 05fc4cc

Please sign in to comment.