188 lines
5.5 KiB
Rust
188 lines
5.5 KiB
Rust
//! Built-in hooks
|
|
//!
|
|
//! This module contains all the low-level built-in hooks that require first party support to work.
|
|
//!
|
|
//! Hooks:
|
|
//! - [`use_hook`]
|
|
//! - [`use_state_provider`]
|
|
//! - [`use_state_consumer`]
|
|
//! - [`use_task`]
|
|
//! - [`use_suspense`]
|
|
|
|
use crate::innerlude::*;
|
|
use futures_util::FutureExt;
|
|
use std::{any::Any, cell::RefCell, future::Future, ops::Deref, rc::Rc};
|
|
|
|
// /// Awaits the given task, forcing the component to re-render when the value is ready.
|
|
// ///
|
|
// /// Returns the handle to the task and the value (if it is ready, else None).
|
|
// ///
|
|
// /// ```
|
|
// /// static Example: FC<()> = |(cx, props)| {
|
|
// /// let (task, value) = use_task(|| async {
|
|
// /// timer::sleep(Duration::from_secs(1)).await;
|
|
// /// "Hello World"
|
|
// /// });
|
|
// ///
|
|
// /// match contents {
|
|
// /// Some(contents) => rsx!(cx, div { "{title}" }),
|
|
// /// None => rsx!(cx, div { "Loading..." }),
|
|
// /// }
|
|
// /// };
|
|
// /// ```
|
|
// pub fn use_coroutine<'src, Out, Fut, Init>(
|
|
// cx: Context<'src>,
|
|
// task_initializer: Init,
|
|
// ) -> (&'src TaskHandle, &'src Option<Out>)
|
|
// where
|
|
// Out: 'static,
|
|
// Fut: Future<Output = Out> + 'static,
|
|
// Init: FnOnce() -> Fut + 'src,
|
|
// {
|
|
// struct TaskHook<T> {
|
|
// handle: TaskHandle,
|
|
// task_dump: Rc<RefCell<Option<T>>>,
|
|
// value: Option<T>,
|
|
// }
|
|
|
|
// todo!()
|
|
|
|
// // // whenever the task is complete, save it into th
|
|
// // cx.use_hook(
|
|
// // move |_| {
|
|
// // let task_fut = task_initializer();
|
|
|
|
// // let task_dump = Rc::new(RefCell::new(None));
|
|
|
|
// // let slot = task_dump.clone();
|
|
|
|
// // let updater = cx.schedule_update_any();
|
|
// // let originator = cx.scope.our_arena_idx;
|
|
|
|
// // let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
|
|
// // *slot.as_ref().borrow_mut() = Some(output);
|
|
// // updater(originator);
|
|
// // originator
|
|
// // })));
|
|
|
|
// // TaskHook {
|
|
// // task_dump,
|
|
// // value: None,
|
|
// // handle,
|
|
// // }
|
|
// // },
|
|
// // |hook| {
|
|
// // if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
|
|
// // hook.value = Some(val);
|
|
// // }
|
|
// // (&hook.handle, &hook.value)
|
|
// // },
|
|
// // |_| {},
|
|
// // )
|
|
// }
|
|
|
|
/// Asynchronously render new nodes once the given future has completed.
|
|
///
|
|
/// # Easda
|
|
///
|
|
///
|
|
///
|
|
///
|
|
/// # Example
|
|
///
|
|
///
|
|
pub fn use_suspense<'src, Out, Fut, Cb>(
|
|
cx: Context<'src>,
|
|
task_initializer: impl FnOnce() -> Fut,
|
|
user_callback: Cb,
|
|
) -> Element
|
|
where
|
|
Fut: Future<Output = Out>,
|
|
Out: 'static,
|
|
Cb: FnMut(&Out) -> Element + 'src,
|
|
{
|
|
/*
|
|
General strategy:
|
|
- Create a slot for the future to dump its output into
|
|
- Create a new future feeding off the user's future that feeds the output into that slot
|
|
- Submit that future as a task
|
|
- Take the task handle id and attach that to our suspended node
|
|
- when the hook runs, check if the value exists
|
|
- if it does, then we can render the node directly
|
|
- if it doesn't, then we render a suspended node along with with the callback and task id
|
|
*/
|
|
todo!()
|
|
// cx.use_hook(
|
|
// move |_| {
|
|
// let value = Rc::new(RefCell::new(None));
|
|
// let slot = value.clone();
|
|
// let originator = cx.scope.our_arena_idx;
|
|
|
|
// let handle = cx.submit_task(Box::pin(task_initializer().then(
|
|
// move |output| async move {
|
|
// *slot.borrow_mut() = Some(Box::new(output) as Box<dyn Any>);
|
|
// originator
|
|
// },
|
|
// )));
|
|
|
|
// SuspenseHook { handle, value }
|
|
// },
|
|
// move |hook| {
|
|
// // If the value exists, just run the callback to get the contents
|
|
// // if the value doesn't exist, we want to render a suspended node with an associated callback
|
|
// if let Some(value) = hook.value.borrow().as_ref() {
|
|
// let out = value.downcast_ref::<Out>().unwrap();
|
|
// user_callback(out)
|
|
// } else {
|
|
// let value = hook.value.clone();
|
|
|
|
// let id = hook.handle.our_id;
|
|
|
|
// let bump = cx.bump();
|
|
|
|
// use bumpalo::boxed::Box as BumpBox;
|
|
|
|
// let f: &mut dyn FnMut() -> Element<'src> = bump.alloc(move || {
|
|
// let val = value.borrow();
|
|
|
|
// let out = val
|
|
// .as_ref()
|
|
// .unwrap()
|
|
// .as_ref()
|
|
// .downcast_ref::<Out>()
|
|
// .unwrap();
|
|
|
|
// user_callback(out)
|
|
// });
|
|
// let callback = unsafe { BumpBox::from_raw(f) };
|
|
|
|
// Some(VNode::Suspended(bump.alloc(VSuspended {
|
|
// dom_id: empty_cell(),
|
|
// task_id: id,
|
|
// callback: RefCell::new(Some(callback)),
|
|
// })))
|
|
// }
|
|
// },
|
|
// |_| {},
|
|
// )
|
|
}
|
|
|
|
// pub(crate) struct SuspenseHook {
|
|
// pub handle: TaskHandle,
|
|
// pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
|
|
// }
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct NodeRef<'src, T: 'static>(&'src RefCell<Option<T>>);
|
|
|
|
impl<'a, T> Deref for NodeRef<'a, T> {
|
|
type Target = RefCell<Option<T>>;
|
|
fn deref(&self) -> &Self::Target {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
pub fn use_node_ref<T, P>(cx: Context) -> NodeRef<T> {
|
|
cx.use_hook(|_| RefCell::new(None), |f| NodeRef { 0: f })
|
|
}
|