server_future uses use_resource

This commit is contained in:
Jonathan Kelley committed 2024-02-02 14:08:21 -08:00
1 parent 0fd7799bc2
commit f44b72f5e1
4 files changed
+64 -99

No files matched your search

+40 -82
View File
@@ -1,99 +1,57 @@
use dioxus_lib::prelude::*;
use serde::{de::DeserializeOwned, Serialize};
use std::cell::Cell;
use std::cell::Ref;
use std::cell::RefCell;
use std::fmt::Debug;
use std::future::Future;
use std::rc::Rc;
use std::sync::Arc;
/// A future that resolves to a value.
///
///
///
/// ```rust
/// fn User(id: String) -> Element {
/// let data = use_sever_future(move || fetch_user(id)).suspend()?;
///
///
/// }
///
/// ```
#[must_use = "Consider using `cx.spawn` to run a future without reading its value"]
pub fn use_server_future<T, F>(_future: impl Fn() -> F) -> UseServerFuture<T>
pub fn use_server_future<T, F>(_future: impl Fn() -> F + 'static) -> Option<Resource<T>>
where
T: Serialize + DeserializeOwned + 'static,
F: Future<Output = T> + 'static,
{
let value: Signal<Option<T>> = use_signal(|| {
// Doesn't this need to be keyed by something?
// We should try and link these IDs across the server and client
// Just the file/line/col span should be fine (or byte index)
#[cfg(feature = "ssr")]
return crate::html_storage::deserialize::take_server_data::<T>();
let mut cb = use_callback(_future);
let mut gen = use_hook(|| CopyValue::new(0));
#[cfg(not(feature = "ssr"))]
return None;
let resource = use_resource(move || {
async move {
// this is going to subscribe this resource to any reactivity given to use in the callback
// We're doing this regardless so inputs get tracked, even if we drop the future before polling it
let user_fut = cb.call();
// If this is the first run, the data might be cached
if gen() == 0 {
#[cfg(not(feature = "web"))]
if let Some(o) = crate::html_storage::deserialize::take_server_data::<T>() {
gen.set(1);
return o;
}
}
// Otherwise just run the future itself
let out = user_fut.await;
// and push the gen forward
gen.set(1);
out
}
});
// Run the callback regardless, giving us the future without actually polling it
// This is where use_server_future gets its reactivity from
// If the client is using signals to drive the future itself, (say, via args to the server_fn), then we need to know
// what signals are being used
use_future(move || async move {
// watch the reactive context
// if it changes, restart the future
//
// if let Err(err) = crate::prelude::server_context().push_html_data(&data) {
// tracing::error!("Failed to push HTML data: {}", err);
// };
// On the first run, force this task to be polled right away in case its value is ready
use_hook(|| {
let _ = resource.task().unwrap().poll_now();
});
// if there's no value ready, mark this component as suspended and return early
if value.peek().is_none() {
suspend();
// Suspend if the value isn't ready
match resource.state() {
UseResourceState::Pending => {
suspend();
None
}
UseResourceState::Regenerating => {
suspend();
Some(resource)
}
UseResourceState::Ready => Some(resource),
}
todo!()
}
pub struct UseServerFuture<T: 'static> {
value: Signal<Option<Signal<T>>>,
}
impl<T> UseServerFuture<T> {
// /// Restart the future with new dependencies.
// ///
// /// Will not cancel the previous future, but will ignore any values that it
// /// generates.
// pub fn restart(&self) {
// self.needs_regen.set(true);
// (self.update)();
// }
// /// Forcefully cancel a future
// pub fn cancel(&self) {
// if let Some(task) = self.task.take() {
// remove_future(task);
// }
// }
/// Return any value, even old values if the future has not yet resolved.
///
/// If the future has never completed, the returned value will be `None`.
pub fn value(&self) -> Option<Signal<T>> {
todo!()
// Ref::map(self.value.read(), |v: &Option<T>| v.as_deref().unwrap())
}
// /// Get the ID of the future in Dioxus' internal scheduler
// pub fn task(&self) -> Option<Task> {
// self.task.get()
// }
// /// Get the current state of the future.
// pub fn reloading(&self) -> bool {
// self.task.get().is_some()
// }
}