From bef4d0dd3bbc01074ebf40829ac989f160b44a21 Mon Sep 17 00:00:00 2001
From: Greg Johnston
Date: Fri, 25 Aug 2023 15:58:19 -0400
Subject: [PATCH 1/6] fix: resource loading signal pattern for subsequent
hydration page loads
---
leptos_reactive/src/hydration.rs | 11 +++++--
leptos_reactive/src/resource.rs | 51 +++++++++++++++++++-------------
2 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/leptos_reactive/src/hydration.rs b/leptos_reactive/src/hydration.rs
index 8eda3e436..8d454bd81 100644
--- a/leptos_reactive/src/hydration.rs
+++ b/leptos_reactive/src/hydration.rs
@@ -9,6 +9,8 @@ use std::collections::{HashMap, HashSet, VecDeque};
/// Hydration data and other context that is shared between the server
/// and the client.
pub struct SharedContext {
+ /// Resources that initially needed to resolve from the server.
+ pub server_resources: HashSet,
/// Resources that have not yet resolved.
pub pending_resources: HashSet,
/// Resources that have already resolved.
@@ -201,24 +203,27 @@ impl Default for SharedContext {
let pending_resources: HashSet = pending_resources
.map_err(|_| ())
.and_then(|pr| serde_wasm_bindgen::from_value(pr).map_err(|_| ()))
- .unwrap_or_default();
+ .unwrap();
let resolved_resources = js_sys::Reflect::get(
&web_sys::window().unwrap(),
&wasm_bindgen::JsValue::from_str("__LEPTOS_RESOLVED_RESOURCES"),
)
- .unwrap_or(wasm_bindgen::JsValue::NULL);
+ .unwrap(); // unwrap_or(wasm_bindgen::JsValue::NULL);
let resolved_resources =
- serde_wasm_bindgen::from_value(resolved_resources).unwrap_or_default();
+ serde_wasm_bindgen::from_value(resolved_resources).unwrap();
+
Self {
+ server_resources: pending_resources.clone(),
pending_resources,
resolved_resources,
pending_fragments: Default::default(),
}
} else {
Self {
+ server_resources: Default::default(),
pending_resources: Default::default(),
resolved_resources: Default::default(),
pending_fragments: Default::default(),
diff --git a/leptos_reactive/src/resource.rs b/leptos_reactive/src/resource.rs
index f948bdb8c..1a5b8a684 100644
--- a/leptos_reactive/src/resource.rs
+++ b/leptos_reactive/src/resource.rs
@@ -504,10 +504,22 @@ where
instrument(level = "trace", skip_all,)
)]
pub fn loading(&self) -> Signal {
- let loading = with_runtime(|runtime| {
- runtime.resource(self.id, |resource: &ResourceState| {
- resource.loading
- })
+ #[allow(unused_variables)]
+ let (loading, is_from_server) = with_runtime(|runtime| {
+ let loading = runtime
+ .resource(self.id, |resource: &ResourceState| {
+ resource.loading
+ });
+ #[cfg(feature = "hydrate")]
+ let is_from_server = runtime
+ .shared_context
+ .borrow()
+ .server_resources
+ .contains(&self.id);
+
+ #[cfg(not(feature = "hydrate"))]
+ let is_from_server = false;
+ (loading, is_from_server)
})
.expect(
"tried to call Resource::loading() in a runtime that has already \
@@ -519,24 +531,20 @@ where
// if the loading signal is read outside Suspense
// in hydrate mode, there will be a mismatch on first render
// unless we delay a tick
- if use_context::().is_none()
- && !loading.get_untracked()
- {
- let (initial, set_initial) = create_signal(true);
- queue_microtask(move || set_initial.set(false));
- Signal::derive(move || {
- if initial.get()
- && use_context::().is_none()
- {
- true
- } else {
- loading.get()
- }
- })
- } else {
- loading.into()
- }
+ let (initial, set_initial) = create_signal(true);
+ queue_microtask(move || set_initial.set(false));
+ Signal::derive(move || {
+ if is_from_server
+ && initial.get()
+ && use_context::().is_none()
+ {
+ true
+ } else {
+ loading.get()
+ }
+ })
}
+
#[cfg(not(feature = "hydrate"))]
{
loading.into()
@@ -785,6 +793,7 @@ where
)
)]
#[inline(always)]
+ #[track_caller]
fn try_get(&self) -> Option
}
+
-
-
- "more >"
-
-
-
+ "more >"
+
+
diff --git a/examples/hackernews/src/routes/story.rs b/examples/hackernews/src/routes/story.rs
index daaffa4c9..313d024f6 100644
--- a/examples/hackernews/src/routes/story.rs
+++ b/examples/hackernews/src/routes/story.rs
@@ -25,48 +25,45 @@ pub fn Story() -> impl IntoView {
};
view! {
- <>
+
-
- {move || story.get().map(|story| match story {
- None => view! { "Error loading this story."
},
- Some(story) => view! {
-
-
-
+ {move || story.get().map(|story| match story {
+ None => view! {
"Error loading this story."
},
+ Some(story) => view! {
+
+
- }})
- }
-
- >
+
+
+ }})}
+
}
}
diff --git a/examples/hackernews_axum/src/routes/stories.rs b/examples/hackernews_axum/src/routes/stories.rs
index d9f0409f0..09ba7ed11 100644
--- a/examples/hackernews_axum/src/routes/stories.rs
+++ b/examples/hackernews_axum/src/routes/stories.rs
@@ -36,12 +36,11 @@ pub fn Stories() -> impl IntoView {
let (pending, set_pending) = create_signal(false);
let hide_more_link = move || {
- pending()
- || stories.get().unwrap_or(None).unwrap_or_default().len() < 28
+ stories.get().unwrap_or(None).unwrap_or_default().len() < 28
+ || pending()
};
view! {
-
@@ -65,20 +64,16 @@ pub fn Stories() -> impl IntoView {
}}
"page " {page}
-
"Loading..." }
+
-
-
- "more >"
-
-
-
+ "more >"
+
+
diff --git a/examples/hackernews_axum/src/routes/story.rs b/examples/hackernews_axum/src/routes/story.rs
index 8e0b3e84e..edeaa9073 100644
--- a/examples/hackernews_axum/src/routes/story.rs
+++ b/examples/hackernews_axum/src/routes/story.rs
@@ -25,48 +25,45 @@ pub fn Story() -> impl IntoView {
};
view! {
- <>
+
-
- {move || story.get().map(|story| match story {
- None => view! { "Error loading this story."
},
- Some(story) => view! {
-
-
-
+ {move || story.get().map(|story| match story {
+ None => view! {
"Error loading this story."
},
+ Some(story) => view! {
+
+
- }})
- }
-
- >
+
+
+ }})}
+
}
}
From 3f3ab1c3c827b81a6d6d9caadf8b275dbe16e614 Mon Sep 17 00:00:00 2001
From: Greg Johnston
Date: Fri, 25 Aug 2023 16:49:26 -0400
Subject: [PATCH 4/6] remove unnecessary parens
---
leptos/src/transition.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/leptos/src/transition.rs b/leptos/src/transition.rs
index c40c45859..49159543e 100644
--- a/leptos/src/transition.rs
+++ b/leptos/src/transition.rs
@@ -130,7 +130,7 @@ where
if is_first_run(&first_run, &suspense_context) {
let has_local_only = suspense_context.has_local_only()
|| cfg!(feature = "csr");
- if (!has_local_only || child_runs.get() > 0) {
+ if !has_local_only || child_runs.get() > 0 {
first_run.set(false);
}
}
From ad6eb58fe100647cb287a1fc885396bd697a52e1 Mon Sep 17 00:00:00 2001
From: Greg Johnston
Date: Fri, 25 Aug 2023 17:12:01 -0400
Subject: [PATCH 5/6] fix: fallback in CSR
---
leptos/src/transition.rs | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/leptos/src/transition.rs b/leptos/src/transition.rs
index 49159543e..ade2d331d 100644
--- a/leptos/src/transition.rs
+++ b/leptos/src/transition.rs
@@ -1,8 +1,8 @@
use leptos_dom::{Fragment, HydrationCtx, IntoView, View};
use leptos_macro::component;
use leptos_reactive::{
- create_isomorphic_effect, use_context, SignalGet, SignalSetter,
- SuspenseContext,
+ create_isomorphic_effect, create_rw_signal, create_signal, use_context,
+ RwSignal, SignalGet, SignalSet, SignalSetter, SuspenseContext,
};
use std::{
cell::{Cell, RefCell},
@@ -83,7 +83,7 @@ where
{
let prev_children = Rc::new(RefCell::new(None::));
- let first_run = Rc::new(std::cell::Cell::new(true));
+ let first_run = create_rw_signal(true);
let child_runs = Cell::new(0);
let held_suspense_context = Rc::new(RefCell::new(None::));
@@ -91,17 +91,18 @@ where
crate::SuspenseProps::builder()
.fallback({
let prev_child = Rc::clone(&prev_children);
- let first_run = Rc::clone(&first_run);
move || {
let suspense_context = use_context::()
.expect("there to be a SuspenseContext");
+ let was_first_run =
+ cfg!(feature = "csr") && first_run.get();
let is_first_run =
- is_first_run(&first_run, &suspense_context);
+ is_first_run(first_run, &suspense_context);
first_run.set(false);
if let Some(prev_children) = &*prev_child.borrow() {
- if is_first_run {
+ if is_first_run || was_first_run {
fallback().into_view()
} else {
prev_children.clone()
@@ -127,10 +128,12 @@ where
{
*prev_children.borrow_mut() = Some(frag.clone());
}
- if is_first_run(&first_run, &suspense_context) {
+ if is_first_run(first_run, &suspense_context) {
let has_local_only = suspense_context.has_local_only()
|| cfg!(feature = "csr");
- if !has_local_only || child_runs.get() > 0 {
+ if (!has_local_only || child_runs.get() > 0)
+ && !cfg!(feature = "csr")
+ {
first_run.set(false);
}
}
@@ -149,7 +152,7 @@ where
}
fn is_first_run(
- first_run: &Rc>,
+ first_run: RwSignal,
suspense_context: &SuspenseContext,
) -> bool {
if cfg!(feature = "csr") {
From 8f067dcde7bf1d4f7fe639e6ee248cfbdb28b795 Mon Sep 17 00:00:00 2001
From: Greg Johnston
Date: Fri, 25 Aug 2023 17:16:00 -0400
Subject: [PATCH 6/6] chore: clear release-mode warnings
---
integrations/actix/src/lib.rs | 1 +
leptos/src/transition.rs | 4 ++--
leptos_macro/src/view/component_builder.rs | 4 +++-
leptos_reactive/src/resource.rs | 4 +++-
leptos_reactive/src/runtime.rs | 9 +++++----
router/src/hooks.rs | 1 +
6 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/integrations/actix/src/lib.rs b/integrations/actix/src/lib.rs
index 9b65d298f..6a4cef273 100644
--- a/integrations/actix/src/lib.rs
+++ b/integrations/actix/src/lib.rs
@@ -27,6 +27,7 @@ use leptos_router::*;
use parking_lot::RwLock;
use regex::Regex;
use std::{fmt::Display, future::Future, sync::Arc};
+#[cfg(debug_assertions)]
use tracing::instrument;
/// This struct lets you define headers and override the status of the Response from an Element or a Server Function
/// Typically contained inside of a ResponseOptions. Setting this is useful for cookies and custom responses.
diff --git a/leptos/src/transition.rs b/leptos/src/transition.rs
index ade2d331d..ca8cc55bd 100644
--- a/leptos/src/transition.rs
+++ b/leptos/src/transition.rs
@@ -1,8 +1,8 @@
use leptos_dom::{Fragment, HydrationCtx, IntoView, View};
use leptos_macro::component;
use leptos_reactive::{
- create_isomorphic_effect, create_rw_signal, create_signal, use_context,
- RwSignal, SignalGet, SignalSet, SignalSetter, SuspenseContext,
+ create_isomorphic_effect, create_rw_signal, use_context, RwSignal,
+ SignalGet, SignalSet, SignalSetter, SuspenseContext,
};
use std::{
cell::{Cell, RefCell},
diff --git a/leptos_macro/src/view/component_builder.rs b/leptos_macro/src/view/component_builder.rs
index 8cb38480e..fc8d876a4 100644
--- a/leptos_macro/src/view/component_builder.rs
+++ b/leptos_macro/src/view/component_builder.rs
@@ -1,6 +1,8 @@
+#[cfg(debug_assertions)]
+use super::ident_from_tag_name;
use super::{
client_builder::{fragment_to_tokens, TagType},
- event_from_attribute_node, ident_from_tag_name,
+ event_from_attribute_node,
};
use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::{format_ident, quote};
diff --git a/leptos_reactive/src/resource.rs b/leptos_reactive/src/resource.rs
index 1a5b8a684..f310fc3de 100644
--- a/leptos_reactive/src/resource.rs
+++ b/leptos_reactive/src/resource.rs
@@ -1,10 +1,12 @@
+#[cfg(debug_assertions)]
+use crate::SpecialNonReactiveZone;
use crate::{
create_effect, create_isomorphic_effect, create_memo, create_signal,
queue_microtask, runtime::with_runtime, serialization::Serializable,
signal_prelude::format_signal_warning, spawn::spawn_local, use_context,
GlobalSuspenseContext, Memo, ReadSignal, ScopeProperty, Signal,
SignalDispose, SignalGet, SignalGetUntracked, SignalSet, SignalUpdate,
- SignalWith, SpecialNonReactiveZone, SuspenseContext, WriteSignal,
+ SignalWith, SuspenseContext, WriteSignal,
};
use std::{
any::Any,
diff --git a/leptos_reactive/src/runtime.rs b/leptos_reactive/src/runtime.rs
index 2e7bdc3f6..f57a26a8f 100644
--- a/leptos_reactive/src/runtime.rs
+++ b/leptos_reactive/src/runtime.rs
@@ -1,12 +1,13 @@
+#[cfg(debug_assertions)]
+use crate::SpecialNonReactiveZone;
use crate::{
hydration::SharedContext,
node::{
Disposer, NodeId, ReactiveNode, ReactiveNodeState, ReactiveNodeType,
},
AnyComputation, AnyResource, EffectState, Memo, MemoState, ReadSignal,
- ResourceId, ResourceState, RwSignal, SerializableResource,
- SpecialNonReactiveZone, StoredValueId, Trigger, UnserializableResource,
- WriteSignal,
+ ResourceId, ResourceState, RwSignal, SerializableResource, StoredValueId,
+ Trigger, UnserializableResource, WriteSignal,
};
use cfg_if::cfg_if;
use core::hash::BuildHasherDefault;
@@ -771,7 +772,7 @@ impl RuntimeId {
pub(crate) fn untrack(
self,
f: impl FnOnce() -> T,
- diagnostics: bool,
+ #[allow(unused)] diagnostics: bool,
) -> T {
with_runtime(|runtime| {
let untracked_result;
diff --git a/router/src/hooks.rs b/router/src/hooks.rs
index 5a1b6e677..aebe38c49 100644
--- a/router/src/hooks.rs
+++ b/router/src/hooks.rs
@@ -190,6 +190,7 @@ pub fn use_navigate() -> impl Fn(&str, NavigateOptions) {
let to = to.to_string();
if cfg!(any(feature = "csr", feature = "hydrate")) {
request_animation_frame(move || {
+ #[allow(unused_variables)]
if let Err(e) = router.navigate_from_route(&to, &options) {
leptos::debug_warn!("use_navigate error: {e:?}");
}
|
- {if story.comments_count.unwrap_or_default() > 0 { - format!("{} comments", story.comments_count.unwrap_or_default()) - } else { - "No comments yet.".into() - }} -
-- }
- />
-
-