fix hydration

This commit is contained in:
Evan Almloff committed 2024-01-15 13:23:35 -06:00
1 parent d17a04931b
commit 940108f242
6 files changed
+83 -36

No files matched your search

+4 -4
View File
@@ -75,8 +75,8 @@ pub use crate::innerlude::{
fc_to_builder, generation, schedule_update, schedule_update_any, use_hook, vdom_is_rendering, fc_to_builder, generation, schedule_update, schedule_update_any, use_hook, vdom_is_rendering,
AnyValue, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element, ElementId, AnyValue, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element, ElementId,
Event, Fragment, HasAttributes, IntoDynNode, Mutation, Mutations, NoOpMutations, Properties, Event, Fragment, HasAttributes, IntoDynNode, Mutation, Mutations, NoOpMutations, Properties,
RenderReturn, ScopeId, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode, RenderReturn, ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, VComponent,
VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations, VNode, VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
}; };
/// The purpose of this module is to alleviate imports of many common types /// The purpose of this module is to alleviate imports of many common types
@@ -89,7 +89,7 @@ pub mod prelude {
push_future, remove_future, schedule_update, schedule_update_any, spawn, spawn_forever, push_future, remove_future, schedule_update, schedule_update_any, spawn, spawn_forever,
suspend, use_error_boundary, use_hook, AnyValue, Attribute, Component, Element, suspend, use_error_boundary, use_hook, AnyValue, Attribute, Component, Element,
ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue, ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue,
IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, Task, Template, TemplateAttribute, IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, ScopeState, Task, Template,
TemplateNode, Throw, VNode, VNodeInner, VirtualDom, TemplateAttribute, TemplateNode, Throw, VNode, VNodeInner, VirtualDom,
}; };
} }
+41
View File
@@ -234,6 +234,47 @@ impl VNode {
} }
} }
} }
/// Get the mounted id for a dynamic node index
pub fn mounted_dynamic_node(
&self,
dynamic_node_idx: usize,
dom: &VirtualDom,
) -> Option<ElementId> {
let mount = self.mount.get().as_usize()?;
match &self.dynamic_nodes[dynamic_node_idx] {
DynamicNode::Text(_) | DynamicNode::Placeholder(_) => dom
.mounts
.get(mount)?
.mounted_dynamic_nodes
.get(dynamic_node_idx)
.map(|id| ElementId(*id)),
_ => None,
}
}
/// Get the mounted id for a root node index
pub fn mounted_root(&self, root_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
let mount = self.mount.get().as_usize()?;
dom.mounts.get(mount)?.root_ids.get(root_idx).copied()
}
/// Get the mounted id for a dynamic attribute index
pub fn mounted_dynamic_attribute(
&self,
dynamic_attribute_idx: usize,
dom: &VirtualDom,
) -> Option<ElementId> {
let mount = self.mount.get().as_usize()?;
dom.mounts
.get(mount)?
.mounted_attributes
.get(dynamic_attribute_idx)
.copied()
}
} }
/// A static layout of a UI tree that describes a set of dynamic and static nodes. /// A static layout of a UI tree that describes a set of dynamic and static nodes.
+3 -3
View File
@@ -147,13 +147,13 @@ impl Runtime {
/// render! { div {} } /// render! { div {} }
/// } /// }
/// ``` /// ```
pub struct RuntimeGuard(Rc<Runtime>); pub struct RuntimeGuard(());
impl RuntimeGuard { impl RuntimeGuard {
/// Create a new runtime guard that sets the current Dioxus runtime. The runtime will be reset when the guard is dropped /// Create a new runtime guard that sets the current Dioxus runtime. The runtime will be reset when the guard is dropped
pub fn new(runtime: Rc<Runtime>) -> Self { pub fn new(runtime: Rc<Runtime>) -> Self {
push_runtime(runtime.clone()); push_runtime(runtime);
Self(runtime) Self(())
} }
/// Run a function with a given runtime and scope in context /// Run a function with a given runtime and scope in context
+3 -3
View File
@@ -12,8 +12,8 @@ pub fn init_eval() {
/// Reprents the ssr-target's provider of evaluators. /// Reprents the ssr-target's provider of evaluators.
pub struct SSREvalProvider; pub struct SSREvalProvider;
impl EvalProvider for SSREvalProvider { impl EvalProvider for SSREvalProvider {
fn new_evaluator(&self, _: String) -> Result<Rc<dyn Evaluator>, EvalError> { fn new_evaluator(&self, _: String) -> Result<Box<dyn Evaluator>, EvalError> {
Ok(Rc::new(SSREvaluator) as Rc<dyn Evaluator + 'static>) Ok(Box::new(SSREvaluator) as Box<dyn Evaluator + 'static>)
} }
} }
@@ -35,7 +35,7 @@ impl Evaluator for SSREvaluator {
} }
/// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript.
async fn recv(&self) -> Result<serde_json::Value, EvalError> { async fn recv(&mut self) -> Result<serde_json::Value, EvalError> {
std::future::pending::<()>().await; std::future::pending::<()>().await;
unreachable!() unreachable!()
} }
+1 -1
View File
@@ -75,7 +75,7 @@ eval = [
[dev-dependencies] [dev-dependencies]
dioxus = { workspace = true } dioxus = { workspace = true }
wasm-bindgen-test = "0.3.29" wasm-bindgen-test = "0.3.29"
dioxus-ssr = { workspace = true} dioxus-ssr = { workspace = true, default-features = false }
gloo-timers = "0.2.3" gloo-timers = "0.2.3"
gloo-dialogs = "0.1.1" gloo-dialogs = "0.1.1"
dioxus-web = { path = ".", features = ["hydrate"] } dioxus-web = { path = ".", features = ["hydrate"] }
+31 -25
View File
@@ -36,10 +36,7 @@ impl WebsysDom {
ids: &mut Vec<u32>, ids: &mut Vec<u32>,
to_mount: &mut Vec<ElementId>, to_mount: &mut Vec<ElementId>,
) -> Result<(), RehydrationError> { ) -> Result<(), RehydrationError> {
let vnode = match scope.root_node() { let vnode = scope.root_node();
dioxus_core::RenderReturn::Ready(ready) => ready,
_ => return Err(VNodeNotInitialized),
};
self.rehydrate_vnode(dom, vnode, ids, to_mount) self.rehydrate_vnode(dom, vnode, ids, to_mount)
} }
@@ -57,7 +54,7 @@ impl WebsysDom {
root, root,
ids, ids,
to_mount, to_mount,
Some(*vnode.root_ids.borrow().get(i).ok_or(VNodeNotInitialized)?), Some(vnode.mounted_root(i, dom).ok_or(VNodeNotInitialized)?),
)?; )?;
} }
Ok(()) Ok(())
@@ -80,9 +77,11 @@ impl WebsysDom {
let mut mounted_id = root_id; let mut mounted_id = root_id;
for attr in *attrs { for attr in *attrs {
if let dioxus_core::TemplateAttribute::Dynamic { id } = attr { if let dioxus_core::TemplateAttribute::Dynamic { id } = attr {
let attribute = &vnode.dynamic_attrs[*id]; let attributes = &*vnode.dynamic_attrs[*id];
let id = attribute.mounted_element(); let id = vnode
attribute.attribute_type().for_each(|attribute| { .mounted_dynamic_attribute(*id, dom)
.ok_or(VNodeNotInitialized)?;
for attribute in attributes {
let value = &attribute.value; let value = &attribute.value;
mounted_id = Some(id); mounted_id = Some(id);
if let AttributeValue::Listener(_) = value { if let AttributeValue::Listener(_) = value {
@@ -90,7 +89,7 @@ impl WebsysDom {
to_mount.push(id); to_mount.push(id);
} }
} }
}); }
} }
} }
if let Some(id) = mounted_id { if let Some(id) = mounted_id {
@@ -102,9 +101,15 @@ impl WebsysDom {
} }
} }
} }
TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => { TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => self
self.rehydrate_dynamic_node(dom, &vnode.dynamic_nodes[*id], ids, to_mount)?; .rehydrate_dynamic_node(
} dom,
&vnode.dynamic_nodes[*id],
*id,
vnode,
ids,
to_mount,
)?,
_ => {} _ => {}
} }
Ok(()) Ok(())
@@ -114,28 +119,29 @@ impl WebsysDom {
&mut self, &mut self,
dom: &VirtualDom, dom: &VirtualDom,
dynamic: &DynamicNode, dynamic: &DynamicNode,
dynamic_node_index: usize,
vnode: &VNode,
ids: &mut Vec<u32>, ids: &mut Vec<u32>,
to_mount: &mut Vec<ElementId>, to_mount: &mut Vec<ElementId>,
) -> Result<(), RehydrationError> { ) -> Result<(), RehydrationError> {
tracing::trace!("rehydrate dynamic node: {:?}", dynamic); tracing::trace!("rehydrate dynamic node: {:?}", dynamic);
match dynamic { match dynamic {
dioxus_core::DynamicNode::Text(text) => { dioxus_core::DynamicNode::Text(_) | dioxus_core::DynamicNode::Placeholder(_) => {
ids.push(text.mounted_element().ok_or(VNodeNotInitialized)?.0 as u32); ids.push(
} vnode
dioxus_core::DynamicNode::Placeholder(placeholder) => { .mounted_dynamic_node(dynamic_node_index, dom)
ids.push(placeholder.mounted_element().ok_or(VNodeNotInitialized)?.0 as u32); .ok_or(VNodeNotInitialized)?
.0 as u32,
);
} }
dioxus_core::DynamicNode::Component(comp) => { dioxus_core::DynamicNode::Component(comp) => {
let scope = comp.mounted_scope().ok_or(VNodeNotInitialized)?; let scope = comp
self.rehydrate_scope( .mounted_scope(dynamic_node_index, vnode, dom)
dom.get_scope(scope).ok_or(VNodeNotInitialized)?, .ok_or(VNodeNotInitialized)?;
dom, self.rehydrate_scope(scope, dom, ids, to_mount)?;
ids,
to_mount,
)?;
} }
dioxus_core::DynamicNode::Fragment(fragment) => { dioxus_core::DynamicNode::Fragment(fragment) => {
for vnode in *fragment { for vnode in fragment {
self.rehydrate_vnode(dom, vnode, ids, to_mount)?; self.rehydrate_vnode(dom, vnode, ids, to_mount)?;
} }
} }