mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-27 16:54:41 -05:00
Compare commits
1 Commits
server-fn-
...
562
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bb3169a69 |
@@ -1,7 +1,10 @@
|
||||
use leptos_dom::{Fragment, IntoView, View};
|
||||
use leptos_macro::component;
|
||||
use leptos_reactive::{Scope, SignalSetter};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use leptos_reactive::{use_context, Scope, SignalSetter, SuspenseContext};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
/// If any [Resource](leptos_reactive::Resource)s are read in the `children` of this
|
||||
/// component, it will show the `fallback` while they are loading. Once all are resolved,
|
||||
@@ -74,26 +77,34 @@ where
|
||||
F: Fn() -> E + 'static,
|
||||
E: IntoView,
|
||||
{
|
||||
let prev_children = std::rc::Rc::new(RefCell::new(None::<Vec<View>>));
|
||||
let prev_children = Rc::new(RefCell::new(None::<Vec<View>>));
|
||||
|
||||
#[cfg(not(feature = "hydrate"))]
|
||||
let first_run = std::cell::Cell::new(true);
|
||||
|
||||
// in hydration mode, "first" run is on the server
|
||||
#[cfg(feature = "hydrate")]
|
||||
let first_run = std::cell::Cell::new(false);
|
||||
let first_run = Rc::new(std::cell::Cell::new(true));
|
||||
let child_runs = Cell::new(0);
|
||||
|
||||
crate::Suspense(
|
||||
cx,
|
||||
crate::SuspenseProps::builder()
|
||||
.fallback({
|
||||
let prev_child = Rc::clone(&prev_children);
|
||||
let first_run = Rc::clone(&first_run);
|
||||
move || {
|
||||
let suspense_context = use_context::<SuspenseContext>(cx)
|
||||
.expect("there to be a SuspenseContext");
|
||||
|
||||
let is_first_run =
|
||||
is_first_run(&first_run, &suspense_context);
|
||||
first_run.set(is_first_run);
|
||||
|
||||
if let Some(set_pending) = &set_pending {
|
||||
set_pending.set(true);
|
||||
}
|
||||
if let Some(prev_children) = &*prev_child.borrow() {
|
||||
prev_children.clone().into_view(cx)
|
||||
if is_first_run {
|
||||
fallback().into_view(cx)
|
||||
} else {
|
||||
prev_children.clone().into_view(cx)
|
||||
}
|
||||
} else {
|
||||
fallback().into_view(cx)
|
||||
}
|
||||
@@ -102,10 +113,19 @@ where
|
||||
.children(Box::new(move |cx| {
|
||||
let frag = children(cx);
|
||||
|
||||
if !first_run.get() {
|
||||
let suspense_context = use_context::<SuspenseContext>(cx)
|
||||
.expect("there to be a SuspenseContext");
|
||||
|
||||
if is_first_run(&first_run, &suspense_context) {
|
||||
let has_local_only = suspense_context.has_local_only();
|
||||
*prev_children.borrow_mut() = Some(frag.nodes.clone());
|
||||
if (has_local_only && child_runs.get() > 0)
|
||||
|| !has_local_only
|
||||
{
|
||||
first_run.set(false);
|
||||
}
|
||||
}
|
||||
first_run.set(false);
|
||||
child_runs.set(child_runs.get() + 1);
|
||||
|
||||
if let Some(set_pending) = &set_pending {
|
||||
set_pending.set(false);
|
||||
@@ -115,3 +135,22 @@ where
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
fn is_first_run(
|
||||
first_run: &Rc<Cell<bool>>,
|
||||
suspense_context: &SuspenseContext,
|
||||
) -> bool {
|
||||
match (
|
||||
first_run.get(),
|
||||
cfg!(feature = "hydrate"),
|
||||
suspense_context.has_local_only(),
|
||||
) {
|
||||
(false, _, _) => false,
|
||||
// is in hydrate mode, and has non-local resources (so, has streamed)
|
||||
(_, false, false) => false,
|
||||
// is in hydrate mode, but with only local resources (so, has not streamed)
|
||||
(_, false, true) => true,
|
||||
// either SSR or client mode: it's the first run
|
||||
(_, true, _) => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,6 +578,12 @@ where
|
||||
let has_value = v.is_some();
|
||||
|
||||
let serializable = self.serializable;
|
||||
if let Some(suspense_cx) = &suspense_cx {
|
||||
if serializable {
|
||||
suspense_cx.has_local_only.set_value(false);
|
||||
}
|
||||
}
|
||||
|
||||
let increment = move |_: Option<()>| {
|
||||
if let Some(s) = &suspense_cx {
|
||||
if let Ok(ref mut contexts) = suspense_contexts.try_borrow_mut()
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
use crate::{
|
||||
create_rw_signal, create_signal, queue_microtask, ReadSignal, RwSignal,
|
||||
Scope, SignalUpdate, WriteSignal,
|
||||
create_rw_signal, create_signal, queue_microtask, store_value, ReadSignal,
|
||||
RwSignal, Scope, SignalUpdate, StoredValue, WriteSignal,
|
||||
};
|
||||
use futures::Future;
|
||||
use std::{borrow::Cow, pin::Pin};
|
||||
@@ -16,6 +16,14 @@ pub struct SuspenseContext {
|
||||
pub pending_resources: ReadSignal<usize>,
|
||||
set_pending_resources: WriteSignal<usize>,
|
||||
pub(crate) pending_serializable_resources: RwSignal<usize>,
|
||||
pub(crate) has_local_only: StoredValue<bool>,
|
||||
}
|
||||
|
||||
impl SuspenseContext {
|
||||
/// Whether the suspense contains local resources at this moment, and therefore can't be
|
||||
pub fn has_local_only(&self) -> bool {
|
||||
self.has_local_only.get_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for SuspenseContext {
|
||||
@@ -37,10 +45,12 @@ impl SuspenseContext {
|
||||
pub fn new(cx: Scope) -> Self {
|
||||
let (pending_resources, set_pending_resources) = create_signal(cx, 0);
|
||||
let pending_serializable_resources = create_rw_signal(cx, 0);
|
||||
let has_local_only = store_value(cx, true);
|
||||
Self {
|
||||
pending_resources,
|
||||
set_pending_resources,
|
||||
pending_serializable_resources,
|
||||
has_local_only,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +58,12 @@ impl SuspenseContext {
|
||||
pub fn increment(&self, serializable: bool) {
|
||||
let setter = self.set_pending_resources;
|
||||
let serializable_resources = self.pending_serializable_resources;
|
||||
let has_local_only = self.has_local_only;
|
||||
queue_microtask(move || {
|
||||
setter.update(|n| *n += 1);
|
||||
if serializable {
|
||||
serializable_resources.update(|n| *n += 1);
|
||||
has_local_only.set_value(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user