Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/core/src/diff/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ impl VirtualDom {
let old_key_to_old_index = old
.iter()
.enumerate()
.map(|(i, o)| (o.key.as_ref().unwrap().as_str(), i))
.map(|(i, o)| (o.key.as_ref().unwrap(), i))
.collect::<FxHashMap<_, _>>();

let mut shared_keys = FxHashSet::default();
Expand All @@ -285,7 +285,7 @@ impl VirtualDom {
.iter()
.map(|node| {
let key = node.key.as_ref().unwrap();
if let Some(&index) = old_key_to_old_index.get(key.as_str()) {
if let Some(&index) = old_key_to_old_index.get(key) {
shared_keys.insert(key);
index
} else {
Expand Down
34 changes: 26 additions & 8 deletions packages/core/src/hotreload_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use std::{
#[cfg(feature = "serialize")]
use crate::nodes::deserialize_string_leaky;
use crate::{
Attribute, AttributeValue, DynamicNode, Template, TemplateAttribute, TemplateNode, VNode, VText,
Attribute, AttributeValue, DynamicNode, Key, Template, TemplateAttribute, TemplateNode, VNode,
VText,
};

#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -261,12 +262,19 @@ impl DynamicValuePool {
}
}

pub fn render_with(&mut self, hot_reload: &HotReloadedTemplate) -> VNode {
pub fn render_with(
&mut self,
hot_reload: &HotReloadedTemplate,
dynamic_key: Option<Key>,
) -> VNode {
// Get the node_paths from a depth first traversal of the template
let key = hot_reload
.key
.as_ref()
.map(|key| self.literal_pool.render_formatted(key));
let key = match &hot_reload.key {
Some(HotReloadKey::Fmted(key)) => {
Some(Key::from(self.literal_pool.render_formatted(key)))
}
Some(HotReloadKey::Dynamic) => dynamic_key,
None => None,
};
let dynamic_nodes = hot_reload
.dynamic_nodes
.iter()
Expand Down Expand Up @@ -345,11 +353,21 @@ pub struct TemplateGlobalKey {

type StaticTemplateArray = &'static [TemplateNode];

#[doc(hidden)]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum HotReloadKey {
/// A formatted string key whose segments can be re-rendered from the literal pool
Fmted(FmtedSegments),
/// An arbitrary hashable expression key computed at the rsx call site
Dynamic,
}

#[doc(hidden)]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct HotReloadedTemplate {
pub key: Option<FmtedSegments>,
pub key: Option<HotReloadKey>,
pub dynamic_nodes: Vec<HotReloadDynamicNode>,
pub dynamic_attributes: Vec<HotReloadDynamicAttribute>,
pub component_values: Vec<HotReloadLiteral>,
Expand All @@ -364,7 +382,7 @@ pub struct HotReloadedTemplate {

impl HotReloadedTemplate {
pub fn new(
key: Option<FmtedSegments>,
key: Option<HotReloadKey>,
dynamic_nodes: Vec<HotReloadDynamicNode>,
dynamic_attributes: Vec<HotReloadDynamicAttribute>,
component_values: Vec<HotReloadLiteral>,
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub mod internal {
#[doc(hidden)]
pub use crate::hotreload_utils::{
DynamicLiteralPool, DynamicValuePool, FmtSegment, FmtedSegments, HotReloadAttributeValue,
HotReloadDynamicAttribute, HotReloadDynamicNode, HotReloadLiteral,
HotReloadDynamicAttribute, HotReloadDynamicNode, HotReloadKey, HotReloadLiteral,
HotReloadTemplateWithLocation, HotReloadedTemplate, HotreloadedLiteral, NamedAttribute,
TemplateGlobalKey,
};
Expand Down Expand Up @@ -95,7 +95,7 @@ pub(crate) mod innerlude {
pub use crate::innerlude::{
AnyValue, AnyhowContext, Attribute, AttributeValue, Callback, CapturedError, Component,
ComponentFunction, DynamicNode, Element, ElementId, ErrorBoundary, ErrorContext, Event,
EventHandler, Fragment, HasAttributes, IntoAttributeValue, IntoDynNode, LaunchConfig,
EventHandler, Fragment, HasAttributes, IntoAttributeValue, IntoDynNode, Key, LaunchConfig,
ListenerCallback, MarkerWrapper, Mutation, Mutations, NoOpMutations, OptionStringFromMarker,
Properties, ReactiveContext, RenderError, Result, Runtime, RuntimeGuard, ScopeId, ScopeState,
SpawnIfAsync, SubscriberList, Subscribers, SuperFrom, SuperInto, SuspendedFuture,
Expand Down
54 changes: 52 additions & 2 deletions packages/core/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use std::vec;
use std::{
any::{Any, TypeId},
cell::Cell,
collections::hash_map::DefaultHasher,
fmt::{Arguments, Debug},
hash::{Hash, Hasher},
};

/// The information about the
Expand All @@ -37,6 +39,54 @@ pub(crate) struct VNodeMount {
pub(crate) mounted_dynamic_nodes: Box<[usize]>,
}

/// A key that uniquely identifies a node among its siblings during keyed diffing.
///
/// Keys are created either from a formatted string (`key: "{value}"`) or from the hash of any
/// value that implements [`Hash`] (`key: value`).
///
/// Two keys only compare equal if they were created the same way: a key created from a string
/// never equals a key created from a hash, even if the hashed value was that same string.
///
/// ```rust
/// use dioxus_core::Key;
///
/// #[derive(Hash)]
/// struct UserId(u64);
///
/// assert_eq!(Key::hashed(UserId(42)), Key::hashed(UserId(42)));
/// assert_ne!(Key::hashed(UserId(42)), Key::hashed(UserId(7)));
/// assert_eq!(Key::from("a"), Key::from("a"));
/// assert_ne!(Key::from("a"), Key::hashed("a"));
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Key {
/// A key rendered from a formatted string like `key: "{value}"`
Str(String),
/// A key created from the hash of an arbitrary hashable value like `key: value`
Hashed(u64),
}

impl Key {
/// Create a key from the hash of any value that implements [`Hash`].
pub fn hashed(value: impl Hash) -> Self {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
Self::Hashed(hasher.finish())
}
}

impl From<String> for Key {
fn from(value: String) -> Self {
Self::Str(value)
}
}

impl From<&str> for Key {
fn from(value: &str) -> Self {
Self::Str(value.to_string())
}
}

/// A reference to a template along with any context needed to hydrate it
///
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
Expand All @@ -46,7 +96,7 @@ pub struct VNodeInner {
/// The key given to the root of this template.
///
/// In fragments, this is the key of the first child. In other cases, it is the key of the root.
pub key: Option<String>,
pub key: Option<Key>,

/// The static nodes and static descriptor of the template
pub template: Template,
Expand Down Expand Up @@ -152,7 +202,7 @@ impl VNode {

/// Create a new VNode
pub fn new(
key: Option<String>,
key: Option<Key>,
template: Template,
dynamic_nodes: Box<[DynamicNode]>,
dynamic_attrs: Box<[Box<[Attribute]>]>,
Expand Down
Loading