use std::cell::Cell;
use std::fmt;
use std::future::Future;
use std::ops::Deref;
use std::rc::Rc;
use yew::prelude::*;
use yew::suspense::{Suspension, SuspensionResult};
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>(init_f: F) -> SuspensionResult<UseFutureHandle<O>>
where
F: FnOnce() -> T,
T: Future<Output = O> + 'static,
O: 'static,
{
use_future_with((), move |_| init_f())
}
#[hook]
pub fn use_future_with<F, D, T, O>(deps: D, f: F) -> SuspensionResult<UseFutureHandle<O>>
where
F: FnOnce(Rc<D>) -> T,
T: Future<Output = O> + 'static,
O: 'static,
D: PartialEq + 'static,
{
let output = use_state(|| None);
let latest_id = use_state(|| Cell::new(0u32));
let suspension = {
let output = output.clone();
use_memo_base(
move |deps| {
let self_id = latest_id.get().wrapping_add(1);
(*latest_id).set(self_id);
let deps = Rc::new(deps);
let task = f(deps.clone());
let suspension = Suspension::from_future(async move {
let result = task.await;
if latest_id.get() == self_id {
output.set(Some(result));
}
});
(suspension, deps)
},
deps,
)
};
if suspension.resumed() {
Ok(UseFutureHandle { inner: output })
} else {
Err((*suspension).clone())
}
}