mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Add use_future hook to make consuming futures as suspense easier (#2609)
* Add `use_suspending_future` hook to make consuming futures as suspense easier * Add test * fmt * use_suspending_future -> use_future * use_future takes a closure
This commit is contained in:
parent
b580bd4c2f
commit
edeb59d777
66
packages/yew/src/suspense/hooks.rs
Normal file
66
packages/yew/src/suspense/hooks.rs
Normal file
@ -0,0 +1,66 @@
|
||||
#[cfg_attr(documenting, doc(cfg(any(target_arch = "wasm32", feature = "tokio"))))]
|
||||
#[cfg(any(target_arch = "wasm32", feature = "tokio"))]
|
||||
mod feat_futures {
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::ops::Deref;
|
||||
|
||||
use yew::prelude::*;
|
||||
use yew::suspense::{Suspension, SuspensionResult};
|
||||
|
||||
/// This hook is used to await a future in a suspending context.
|
||||
///
|
||||
/// A [Suspension] is created from the passed future and the result of the future
|
||||
/// is the output of the suspension.
|
||||
pub struct UseFutureHandle<O> {
|
||||
inner: UseStateHandle<Option<O>>,
|
||||
}
|
||||
|
||||
impl<O> Deref for UseFutureHandle<O> {
|
||||
type Target = O;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.inner.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UseFutureHandle<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("UseFutureHandle")
|
||||
.field("value", &format!("{:?}", self.inner))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[hook]
|
||||
pub fn use_future<F, T, O>(f: F) -> SuspensionResult<UseFutureHandle<O>>
|
||||
where
|
||||
F: FnOnce() -> T + 'static,
|
||||
T: Future<Output = O> + 'static,
|
||||
O: 'static,
|
||||
{
|
||||
let output = use_state(|| None);
|
||||
|
||||
let suspension = {
|
||||
let output = output.clone();
|
||||
|
||||
use_memo(
|
||||
move |_| {
|
||||
Suspension::from_future(async move {
|
||||
output.set(Some(f().await));
|
||||
})
|
||||
},
|
||||
(),
|
||||
)
|
||||
};
|
||||
|
||||
if suspension.resumed() {
|
||||
Ok(UseFutureHandle { inner: output })
|
||||
} else {
|
||||
Err((*suspension).clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", feature = "tokio"))]
|
||||
pub use feat_futures::*;
|
||||
@ -1,9 +1,11 @@
|
||||
//! This module provides suspense support.
|
||||
|
||||
mod component;
|
||||
mod hooks;
|
||||
mod suspension;
|
||||
|
||||
#[cfg(any(feature = "csr", feature = "ssr"))]
|
||||
pub(crate) use component::BaseSuspense;
|
||||
pub use component::Suspense;
|
||||
pub use hooks::*;
|
||||
pub use suspension::{Suspension, SuspensionHandle, SuspensionResult};
|
||||
|
||||
@ -15,7 +15,7 @@ use gloo::timers::future::TimeoutFuture;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use web_sys::{HtmlElement, HtmlTextAreaElement};
|
||||
use yew::suspense::{Suspension, SuspensionResult};
|
||||
use yew::suspense::{use_future, Suspension, SuspensionResult};
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn suspense_works() {
|
||||
@ -593,3 +593,44 @@ async fn effects_not_run_when_suspended() {
|
||||
);
|
||||
assert_eq!(*counter.borrow(), 4); // effects ran 4 times.
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn use_suspending_future_works() {
|
||||
#[function_component(Content)]
|
||||
fn content() -> HtmlResult {
|
||||
let _sleep_handle = use_future(|| async move {
|
||||
TimeoutFuture::new(50).await;
|
||||
})?;
|
||||
|
||||
Ok(html! {
|
||||
<div>
|
||||
{"Content"}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
let fallback = html! {<div>{"wait..."}</div>};
|
||||
|
||||
html! {
|
||||
<div id="result">
|
||||
<Suspense {fallback}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
yew::Renderer::<App>::with_root(gloo_utils::document().get_element_by_id("output").unwrap())
|
||||
.render();
|
||||
|
||||
TimeoutFuture::new(10).await;
|
||||
let result = obtain_result();
|
||||
assert_eq!(result.as_str(), "<div>wait...</div>");
|
||||
|
||||
TimeoutFuture::new(50).await;
|
||||
|
||||
let result = obtain_result();
|
||||
assert_eq!(result.as_str(), r#"<div>Content</div>"#);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user