Properly implement wait_for_suspense
Wait_for_suspense now does not call flush_sync, making it appropriate for deferring effects on the server. Futures will still run on the server (as needed to progress suspense) but use_effect / and memo watchers will not run on the server
This commit is contained in:
11 files changed
+156
-86
No files matched your search
@@ -81,3 +81,6 @@ pub use use_resource::*;
|
||||
|
||||
mod use_root_context;
|
||||
pub use use_root_context::*;
|
||||
|
||||
mod use_hook_did_run;
|
||||
pub use use_hook_did_run::*;
|
||||
@@ -1,14 +1,12 @@
|
||||
#![allow(missing_docs)]
|
||||
use crate::use_hook_did_run;
|
||||
use dioxus_core::{
|
||||
prelude::{spawn, use_before_render, use_drop, use_hook},
|
||||
ScopeState, Task,
|
||||
prelude::{spawn, use_drop, use_hook},
|
||||
Task,
|
||||
};
|
||||
use dioxus_signals::*;
|
||||
use dioxus_signals::{Readable, Writable};
|
||||
use futures_util::{future, pin_mut, FutureExt};
|
||||
use std::{any::Any, cell::Cell, future::Future, pin::Pin, rc::Rc, sync::Arc, task::Poll};
|
||||
|
||||
use crate::use_callback;
|
||||
use std::future::Future;
|
||||
|
||||
/// A hook that allows you to spawn a future
|
||||
///
|
||||
@@ -17,44 +15,28 @@ pub fn use_future<F>(mut future: impl FnMut() -> F) -> UseFuture
|
||||
where
|
||||
F: Future + 'static,
|
||||
{
|
||||
let state = use_signal(|| UseFutureState::Pending);
|
||||
let mut state = use_signal(|| UseFutureState::Pending);
|
||||
|
||||
// Create the task inside a copyvalue so we can reset it in-place later
|
||||
let task = use_hook(|| {
|
||||
let fut = future();
|
||||
CopyValue::new(spawn(async move {
|
||||
fut.await;
|
||||
state.set(UseFutureState::Complete);
|
||||
}))
|
||||
});
|
||||
|
||||
/*
|
||||
Early returns in dioxus have consequences for use_memo, use_resource, and use_future, etc
|
||||
We *don't* want futures to be running if the component early returns. It's a rather weird behavior to have
|
||||
use_memo running in the background even if the component isn't hitting those hooks anymore.
|
||||
|
||||
React solves this by simply not having early returns interleave with hooks.
|
||||
However, since dioxus allows early returns (since we use them for suspense), we need to solve this problem.
|
||||
|
||||
|
||||
*/
|
||||
// Track if this *current* render is the same
|
||||
let gen = use_hook(|| CopyValue::new((0, 0)));
|
||||
|
||||
// Early returns will pause this task, effectively
|
||||
use_before_render(move || {
|
||||
gen.write().0 += 1;
|
||||
task.peek().set_active(false);
|
||||
// Early returns in dioxus have consequences for use_memo, use_resource, and use_future, etc
|
||||
// We *don't* want futures to be running if the component early returns. It's a rather weird behavior to have
|
||||
// use_memo running in the background even if the component isn't hitting those hooks anymore.
|
||||
//
|
||||
// React solves this by simply not having early returns interleave with hooks.
|
||||
// However, since dioxus allows early returns (since we use them for suspense), we need to solve this problem
|
||||
use_hook_did_run(move |did_run| match did_run {
|
||||
true => task.peek().resume(),
|
||||
false => task.peek().pause(),
|
||||
});
|
||||
|
||||
// However when we actually run this component, we want to resume the task
|
||||
task.peek().set_active(true);
|
||||
gen.write().1 += 1;
|
||||
|
||||
// if the gens are different, we need to wake the task
|
||||
if gen().0 != gen().1 {
|
||||
task.peek().wake();
|
||||
}
|
||||
|
||||
use_drop(move || task.peek().stop());
|
||||
|
||||
UseFuture { task, state }
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_signals::{CopyValue, Writable};
|
||||
|
||||
/// A hook that uses before/after lifecycle hooks to determine if the hook was run
|
||||
pub fn use_hook_did_run(mut handler: impl FnMut(bool) + 'static) {
|
||||
let mut did_run_ = use_hook(|| CopyValue::new(false));
|
||||
|
||||
// Before render always set the value to false
|
||||
use_before_render(move || did_run_.set(false));
|
||||
|
||||
// Only when this hook is hit do we want to set the value to true
|
||||
did_run_.set(true);
|
||||
|
||||
// After render, we can check if the hook was run
|
||||
use_after_render(move || handler(did_run_()));
|
||||
}
|
||||
Reference in new issue
Block a user