fix: tweak bounds on For for backwards-compat (#3663)

This commit is contained in:
Greg Johnston
2025-03-01 11:50:09 -05:00
committed by GitHub
parent 98e00fcb3b
commit 3dbb251853
6 changed files with 55 additions and 10 deletions

4
Cargo.lock generated
View File

@@ -1808,6 +1808,7 @@ dependencies = [
"send_wrapper",
"serde_json",
"server_fn",
"tachys",
"tokio",
"tracing",
]
@@ -1829,6 +1830,7 @@ dependencies = [
"once_cell",
"parking_lot",
"server_fn",
"tachys",
"tokio",
"tower",
"tower-http",
@@ -3450,6 +3452,8 @@ dependencies = [
"reactive_stores",
"rustc-hash 2.1.1",
"send_wrapper",
"serde",
"serde_json",
"sledgehammer_bindgen",
"sledgehammer_utils",
"slotmap",

View File

@@ -21,6 +21,7 @@ leptos_macro = { workspace = true, features = ["actix"] }
leptos_meta = { workspace = true, features = ["nonce"] }
leptos_router = { workspace = true, features = ["ssr"] }
server_fn = { workspace = true, features = ["actix"] }
tachys = { workspace = true }
serde_json = { workspace = true }
parking_lot = "0.12.3"
tracing = { version = "0.1", optional = true }
@@ -33,7 +34,7 @@ once_cell = "1"
rustdoc-args = ["--generate-link-to-definition"]
[features]
islands-router = []
islands-router = ["tachys/islands"]
tracing = ["dep:tracing"]
[package.metadata.cargo-all-features]

View File

@@ -22,6 +22,7 @@ leptos_macro = { workspace = true, features = ["axum"] }
leptos_meta = { workspace = true, features = ["ssr", "nonce"] }
leptos_router = { workspace = true, features = ["ssr"] }
leptos_integration_utils = { workspace = true }
tachys = { workspace = true }
once_cell = "1"
parking_lot = "0.12.3"
tokio = { version = "1.43", default-features = false }
@@ -36,7 +37,7 @@ tokio = { version = "1.43", features = ["net", "rt-multi-thread"] }
[features]
wasm = []
default = ["tokio/fs", "tokio/sync", "tower-http/fs", "tower/util"]
islands-router = []
islands-router = ["tachys/islands"]
tracing = ["dep:tracing"]
[package.metadata.docs.rs]

View File

@@ -6,7 +6,10 @@ use reactive_graph::{
traits::Set,
};
use std::hash::Hash;
use tachys::{reactive_graph::OwnedView, view::keyed::keyed};
use tachys::{
reactive_graph::OwnedView,
view::keyed::{keyed, SerializableKey},
};
/// Iterates over children and displays them, keyed by the `key` function given.
///
@@ -121,7 +124,7 @@ where
EF: Fn(T) -> N + Send + Clone + 'static,
N: IntoView + 'static,
KF: Fn(&T) -> K + Send + Clone + 'static,
K: Eq + Hash + ToString + 'static,
K: Eq + Hash + SerializableKey + 'static,
T: Send + 'static,
{
// this takes the owner of the For itself
@@ -195,7 +198,7 @@ where
EF: Fn(ReadSignal<usize>, T) -> N + Send + Clone + 'static,
N: IntoView + 'static,
KF: Fn(&T) -> K + Send + Clone + 'static,
K: Eq + Hash + ToString + 'static,
K: Eq + Hash + SerializableKey + 'static,
T: Send + 'static,
{
// this takes the owner of the For itself
@@ -218,6 +221,7 @@ where
};
move || keyed(each(), key.clone(), children.clone())
}
/*
#[cfg(test)]
mod tests {

View File

@@ -162,6 +162,8 @@ sledgehammer_bindgen = { version = "0.6.0", features = [
], optional = true }
sledgehammer_utils = { version = "0.3.1", optional = true }
tracing = { version = "0.1.41", optional = true }
serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true }
[dev-dependencies]
tokio-test = "0.4.4"
@@ -172,7 +174,7 @@ default = ["testing"]
delegation = [] # enables event delegation
error-hook = []
hydrate = []
islands = []
islands = ["dep:serde", "dep:serde_json"]
ssr = []
oco = ["dep:oco_ref"]
nightly = ["reactive_graph/nightly"]

View File

@@ -50,6 +50,39 @@ where
view_fn: VF,
}
/// By default, keys used in for keyed iteration do not need to be serializable.
///
/// However, for some scenarios (like the “islands routing” mode that mixes server-side
/// rendering with client-side navigation) it is useful to have serializable keys.
///
/// When the `islands` feature is not enabled, this trait is implemented by all types.
///
/// When the `islands` features is enabled, this is automatically implemented for all types
/// that implement [`Serialize`](serde::Serialize), and can be manually implemented otherwise.
pub trait SerializableKey {
/// Serializes the key to a unique string.
///
/// The string can have any value, as long as it is idempotent (i.e., serializing the same key
/// multiple times will give the same value).
fn ser_key(&self) -> String;
}
#[cfg(not(feature = "islands"))]
impl<T> SerializableKey for T {
fn ser_key(&self) -> String {
panic!(
"SerializableKey called without the `islands` feature enabled. \
Something has gone wrong."
);
}
}
#[cfg(feature = "islands")]
impl<T: serde::Serialize> SerializableKey for T {
fn ser_key(&self) -> String {
serde_json::to_string(self).expect("failed to serialize key")
}
}
/// Retained view state for a keyed list.
pub struct KeyedState<K, VFS, V>
where
@@ -66,7 +99,7 @@ where
impl<T, I, K, KF, VF, VFS, V> Render for Keyed<T, I, K, KF, VF, VFS, V>
where
I: IntoIterator<Item = T>,
K: Eq + Hash + ToString + 'static,
K: Eq + Hash + SerializableKey + 'static,
KF: Fn(&T) -> K,
V: Render,
VF: Fn(usize, T) -> (VFS, V),
@@ -132,7 +165,7 @@ where
impl<T, I, K, KF, VF, VFS, V> AddAnyAttr for Keyed<T, I, K, KF, VF, VFS, V>
where
I: IntoIterator<Item = T> + Send + 'static,
K: Eq + Hash + ToString + 'static,
K: Eq + Hash + SerializableKey + 'static,
KF: Fn(&T) -> K + Send + 'static,
V: RenderHtml,
V: 'static,
@@ -185,7 +218,7 @@ where
impl<T, I, K, KF, VF, VFS, V> RenderHtml for Keyed<T, I, K, KF, VF, VFS, V>
where
I: IntoIterator<Item = T> + Send + 'static,
K: Eq + Hash + ToString + 'static,
K: Eq + Hash + SerializableKey + 'static,
KF: Fn(&T) -> K + Send + 'static,
V: RenderHtml + 'static,
VF: Fn(usize, T) -> (VFS, V) + Send + 'static,
@@ -261,7 +294,7 @@ where
for (index, item) in self.items.into_iter().enumerate() {
let branch_name = mark_branches.then(|| {
let key = (self.key_fn)(&item);
let key = key.to_string();
let key = key.ser_key();
format!("item-{key}")
});
let (_, item) = (self.view_fn)(index, item);