fix suspense

This commit is contained in:
Evan Almloff committed 2023-07-17 16:48:54 -07:00
1 parent 913d1f0491
commit dd5d974aeb
12 files changed
+351 -110

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
pub mod server_cached;
pub mod server_future;
@@ -0,0 +1,34 @@
use serde::{de::DeserializeOwned, Serialize};
/// This allows you to send data from the server to the client. The data is serialized into the HTML on the server and hydrated on the client.
///
/// When you run this function on the client, you need to be careful to insure the order you run it initially is the same order you run it on the server.
///
/// If Dioxus fullstack cannot find the data on the client, it will run the closure again to get the data.
///
/// # Example
/// ```rust
/// use dioxus::prelude::*;
/// use dioxus_fullstack::prelude::*;
///
/// fn app(cx: Scope) -> Element {
/// let state1 = use_state(cx, || from_server(|| {
/// 1234
/// }));
/// }
/// ```
pub fn server_cached<O: 'static + Serialize + DeserializeOwned>(server_fn: impl Fn() -> O) -> O {
#[cfg(feature = "ssr")]
{
let data = server_fn();
let sc = crate::prelude::server_context();
if let Err(err) = sc.push_html_data(&data) {
log::error!("Failed to push HTML data: {}", err);
}
data
}
#[cfg(not(feature = "ssr"))]
{
crate::html_storage::deserialize::take_server_data().unwrap_or_else(|| server_fn())
}
}
@@ -0,0 +1,146 @@
use dioxus::prelude::*;
use serde::{de::DeserializeOwned, Serialize};
use std::any::Any;
use std::cell::Cell;
use std::cell::Ref;
use std::cell::RefCell;
use std::future::Future;
use std::rc::Rc;
use std::sync::Arc;
/// A future that resolves to a value.
///
/// This runs the future only once - though the future may be regenerated
/// through the [`UseServerFuture::restart`] method.
///
/// This is commonly used for components that cannot be rendered until some
/// asynchronous operation has completed.
///
/// Whenever the hooks dependencies change, the future will be re-evaluated.
/// If a future is pending when the dependencies change, the previous future
/// will be allowed to continue
///
/// - dependencies: a tuple of references to values that are PartialEq + Clone
pub fn use_server_future<T, F, D>(
cx: &ScopeState,
dependencies: D,
future: impl FnOnce(D::Out) -> F,
) -> Option<&UseServerFuture<T>>
where
T: 'static + Serialize + DeserializeOwned,
F: Future<Output = T> + 'static,
D: UseFutureDep,
{
let state = cx.use_hook(move || UseServerFuture {
update: cx.schedule_update(),
needs_regen: Cell::new(true),
value: Default::default(),
task: Cell::new(None),
dependencies: Vec::new(),
});
let first_run = { state.value.borrow().as_ref().is_none() };
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen.get() {
// We don't need regen anymore
state.needs_regen.set(false);
// Create the new future
let fut = future(dependencies.out());
// Clone in our cells
let value = state.value.clone();
let schedule_update = state.update.clone();
// Cancel the current future
if let Some(current) = state.task.take() {
cx.remove_future(current);
}
state.task.set(Some(cx.push_future(async move {
let data;
#[cfg(feature = "ssr")]
{
data = fut.await;
if first_run {
if let Err(err) = crate::prelude::server_context().push_html_data(&data) {
log::error!("Failed to push HTML data: {}", err);
};
}
}
#[cfg(not(feature = "ssr"))]
{
data = match crate::html_storage::deserialize::take_server_data() {
Some(data) => data,
None => fut.await,
};
}
*value.borrow_mut() = Some(Box::new(data));
schedule_update();
})));
}
if first_run {
log::trace!("Suspending first run of use_server_future");
cx.suspend();
None
} else {
Some(state)
}
}
pub enum FutureState<'a, T> {
Pending,
Complete(&'a T),
Regenerating(&'a T), // the old value
}
pub struct UseServerFuture<T> {
update: Arc<dyn Fn()>,
needs_regen: Cell<bool>,
task: Cell<Option<TaskId>>,
dependencies: Vec<Box<dyn Any>>,
value: Rc<RefCell<Option<Box<T>>>>,
}
pub enum UseFutureState<'a, T> {
Pending,
Complete(&'a T),
Reloading(&'a 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, cx: &ScopeState) {
if let Some(task) = self.task.take() {
cx.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) -> Ref<'_, T> {
Ref::map(self.value.borrow(), |v| v.as_deref().unwrap())
}
/// Get the ID of the future in Dioxus' internal scheduler
pub fn task(&self) -> Option<TaskId> {
self.task.get()
}
/// Get the current state of the future.
pub fn reloading(&self) -> bool {
self.task.get().is_some()
}
}