From 3c19def5500c2dae56d90ca8e4f05caf9e39ac8e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 30 Nov 2022 11:24:13 -0500 Subject: [PATCH] chore: get create working and simplify dynamic nodes --- examples/router.rs | 4 +- examples/rsx_usage.rs | 4 +- packages/core/src/arena.rs | 4 +- packages/core/src/create.rs | 50 +++++++-------- packages/core/src/diff.rs | 71 +++++--------------- packages/core/src/factory.rs | 28 +++++--- packages/core/src/lib.rs | 3 +- packages/core/src/mutations.rs | 4 -- packages/core/src/nodes.rs | 12 ++-- packages/core/src/scope_arena.rs | 4 +- packages/desktop/src/controller.rs | 2 +- packages/interpreter/src/interpreter.js | 75 ++++++++++++++-------- packages/router/src/components/redirect.rs | 2 +- packages/router/src/components/route.rs | 5 +- packages/ssr/src/cache.rs | 2 + packages/ssr/src/template.rs | 71 ++++++++++---------- 16 files changed, 168 insertions(+), 173 deletions(-) diff --git a/examples/router.rs b/examples/router.rs index 20b168670..a4118111a 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -30,7 +30,7 @@ fn app(cx: Scope) -> Element { } fn BlogPost(cx: Scope) -> Element { - let post = dioxus_router::use_route(&cx).last_segment()?; + let post = dioxus_router::use_route(&cx).last_segment().unwrap(); cx.render(rsx! { div { @@ -46,7 +46,7 @@ struct Query { } fn User(cx: Scope) -> Element { - let post = dioxus_router::use_route(&cx).last_segment()?; + let post = dioxus_router::use_route(&cx).last_segment().unwrap(); let query = dioxus_router::use_route(&cx) .query::() diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 597c6ab79..99598479f 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -165,13 +165,13 @@ fn app(cx: Scope) -> Element { // Can pass in props directly as an expression { - let props = TallerProps {a: "hello", children: Default::default()}; + let props = TallerProps {a: "hello", children: cx.render(rsx!(()))}; rsx!(Taller { ..props }) } // Spreading can also be overridden manually Taller { - ..TallerProps { a: "ballin!", children: Default::default() }, + ..TallerProps { a: "ballin!", children: cx.render(rsx!(()) )}, a: "not ballin!" } diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs index c6796ed30..c27dca5ed 100644 --- a/packages/core/src/arena.rs +++ b/packages/core/src/arena.rs @@ -1,6 +1,6 @@ use crate::{ factory::RenderReturn, nodes::VNode, virtual_dom::VirtualDom, AttributeValue, DynamicNode, - ScopeId, VFragment, + ScopeId, }; use bumpalo::boxed::Box as BumpBox; @@ -101,7 +101,7 @@ impl VirtualDom { for (idx, _) in node.template.roots.iter().enumerate() { match node.dynamic_root(idx) { Some(DynamicNode::Component(c)) => self.drop_scope(c.scope.get().unwrap()), - Some(DynamicNode::Fragment(VFragment::NonEmpty(nodes))) => { + Some(DynamicNode::Fragment(nodes)) => { for node in *nodes { self.drop_scope_inner(node); } diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index de309221f..771b1dfa6 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -1,11 +1,13 @@ +use std::cell::Cell; + use crate::factory::RenderReturn; -use crate::innerlude::{VComponent, VFragment, VText}; +use crate::innerlude::{VComponent, VText}; use crate::mutations::Mutation; use crate::mutations::Mutation::*; use crate::nodes::VNode; use crate::nodes::{DynamicNode, TemplateNode}; use crate::virtual_dom::VirtualDom; -use crate::{AttributeValue, ScopeId, SuspenseContext, TemplateAttribute}; +use crate::{AttributeValue, ElementId, ScopeId, SuspenseContext, TemplateAttribute}; impl<'b> VirtualDom { /// Create a new template [`VNode`] and write it to the [`Mutations`] buffer. @@ -55,15 +57,14 @@ impl<'b> VirtualDom { 1 } - DynamicNode::Fragment(VFragment::Empty(slot)) => { + DynamicNode::Placeholder(slot) => { let id = self.next_element(template, template.template.node_paths[*id]); slot.set(id); self.mutations.push(CreatePlaceholder { id }); 1 } - DynamicNode::Fragment(VFragment::NonEmpty(_)) - | DynamicNode::Component { .. } => { + DynamicNode::Fragment(_) | DynamicNode::Component { .. } => { self.create_dynamic_node(template, &template.dynamic_nodes[*id], *id) } } @@ -309,7 +310,8 @@ impl<'b> VirtualDom { use DynamicNode::*; match node { Text(text) => self.create_dynamic_text(template, text, idx), - Fragment(frag) => self.create_fragment(frag, template, idx), + Fragment(frag) => self.create_fragment(frag), + Placeholder(frag) => self.create_placeholder(frag, template, idx), Component(component) => self.create_component_node(template, component, idx), } } @@ -340,34 +342,30 @@ impl<'b> VirtualDom { 0 } - pub(crate) fn create_fragment( + pub(crate) fn create_placeholder( &mut self, - frag: &'b VFragment<'b>, + slot: &Cell, template: &'b VNode<'b>, idx: usize, ) -> usize { - match frag { - VFragment::NonEmpty(nodes) => { - nodes.iter().fold(0, |acc, child| acc + self.create(child)) - } + // Allocate a dynamic element reference for this text node + let id = self.next_element(template, template.template.node_paths[idx]); - VFragment::Empty(slot) => { - // Allocate a dynamic element reference for this text node - let id = self.next_element(template, template.template.node_paths[idx]); + // Make sure the text node is assigned to the correct element + slot.set(id); - // Make sure the text node is assigned to the correct element - slot.set(id); + // Assign the ID to the existing node in the template + self.mutations.push(AssignId { + path: &template.template.node_paths[idx][1..], + id, + }); - // Assign the ID to the existing node in the template - self.mutations.push(AssignId { - path: &template.template.node_paths[idx][1..], - id, - }); + // Since the placeholder is already in the DOM, we don't create any new nodes + 0 + } - // Since the placeholder is already in the DOM, we don't create any new nodes - 0 - } - } + pub(crate) fn create_fragment(&mut self, nodes: &'b [VNode<'b>]) -> usize { + nodes.iter().fold(0, |acc, child| acc + self.create(child)) } pub(super) fn create_component_node( diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 656df0fd1..5ed82fbcd 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -1,7 +1,7 @@ use crate::{ arena::ElementId, factory::RenderReturn, - innerlude::{DirtyScope, VComponent, VFragment, VText}, + innerlude::{DirtyScope, VComponent, VText}, mutations::Mutation, nodes::{DynamicNode, VNode}, scopes::ScopeId, @@ -10,7 +10,6 @@ use crate::{ }; use fxhash::{FxHashMap, FxHashSet}; -use std::cell::Cell; use DynamicNode::*; impl<'b> VirtualDom { @@ -101,7 +100,7 @@ impl<'b> VirtualDom { { match (left_node, right_node) { (Text(left), Text(right)) => self.diff_vtext(left, right), - (Fragment(left), Fragment(right)) => self.diff_vfragment(left, right), + (Fragment(left), Fragment(right)) => self.diff_non_empty_fragment(left, right), (Component(left), Component(right)) => { self.diff_vcomponent(left, right, right_template, idx) } @@ -231,40 +230,6 @@ impl<'b> VirtualDom { } } - fn diff_vfragment(&mut self, left: &'b VFragment<'b>, right: &'b VFragment<'b>) { - use VFragment::*; - match (left, right) { - (Empty(l), Empty(r)) => r.set(l.get()), - (Empty(l), NonEmpty(r)) => self.replace_placeholder_with_nodes(l, r), - (NonEmpty(l), Empty(r)) => self.replace_nodes_with_placeholder(l, r), - (NonEmpty(old), NonEmpty(new)) => self.diff_non_empty_fragment(new, old), - } - } - - fn replace_placeholder_with_nodes(&mut self, l: &'b Cell, r: &'b [VNode<'b>]) { - let m = self.create_children(r); - let id = l.get(); - self.mutations.push(Mutation::ReplaceWith { id, m }); - self.reclaim(id); - } - - fn replace_nodes_with_placeholder(&mut self, l: &'b [VNode<'b>], r: &'b Cell) { - // Create the placeholder first, ensuring we get a dedicated ID for the placeholder - let placeholder = self.next_element(&l[0], &[]); - r.set(placeholder); - self.mutations - .push(Mutation::CreatePlaceholder { id: placeholder }); - - // Remove the old nodes, except for onea - let first = self.replace_inner(&l[0]); - self.remove_nodes(&l[1..]); - - self.mutations - .push(Mutation::ReplaceWith { id: first, m: 1 }); - - self.try_reclaim(first); - } - /// Remove all the top-level nodes, returning the firstmost root ElementId /// /// All IDs will be garbage collected @@ -272,8 +237,8 @@ impl<'b> VirtualDom { let id = match node.dynamic_root(0) { None => node.root_ids[0].get(), Some(Text(t)) => t.id.get(), - Some(Fragment(VFragment::Empty(e))) => e.get(), - Some(Fragment(VFragment::NonEmpty(nodes))) => { + Some(Placeholder(e)) => e.get(), + Some(Fragment(nodes)) => { let id = self.replace_inner(&nodes[0]); self.remove_nodes(&nodes[1..]); id @@ -317,10 +282,8 @@ impl<'b> VirtualDom { }; } Text(t) => self.reclaim(t.id.get()), - Fragment(VFragment::Empty(t)) => self.reclaim(t.get()), - Fragment(VFragment::NonEmpty(nodes)) => { - nodes.iter().for_each(|node| self.clean_up_node(node)) - } + Placeholder(t) => self.reclaim(t.get()), + Fragment(nodes) => nodes.iter().for_each(|node| self.clean_up_node(node)), }; } @@ -351,12 +314,12 @@ impl<'b> VirtualDom { self.mutations.push(Mutation::Remove { id }); self.reclaim(id); } - Some(Fragment(VFragment::Empty(e))) => { + Some(Placeholder(e)) => { let id = e.get(); self.mutations.push(Mutation::Remove { id }); self.reclaim(id); } - Some(Fragment(VFragment::NonEmpty(nodes))) => self.remove_nodes(nodes), + Some(Fragment(nodes)) => self.remove_nodes(nodes), Some(Component(comp)) => { let scope = comp.scope.get().unwrap(); match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } { @@ -750,8 +713,8 @@ impl<'b> VirtualDom { for (idx, _) in node.template.roots.iter().enumerate() { let id = match node.dynamic_root(idx) { Some(Text(t)) => t.id.get(), - Some(Fragment(VFragment::Empty(t))) => t.get(), - Some(Fragment(VFragment::NonEmpty(t))) => return self.remove_nodes(t), + Some(Placeholder(t)) => t.get(), + Some(Fragment(t)) => return self.remove_nodes(t), Some(Component(comp)) => return self.remove_component(comp.scope.get().unwrap()), None => node.root_ids[idx].get(), }; @@ -793,12 +756,12 @@ impl<'b> VirtualDom { self.mutations.push(Mutation::PushRoot { id: t.id.get() }); onstack += 1; } - Some(Fragment(VFragment::Empty(t))) => { + Some(Placeholder(t)) => { self.mutations.push(Mutation::PushRoot { id: t.get() }); onstack += 1; } - Some(Fragment(VFragment::NonEmpty(t))) => { - for node in *t { + Some(Fragment(nodes)) => { + for node in *nodes { onstack += self.push_all_real_nodes(node); } } @@ -842,8 +805,8 @@ impl<'b> VirtualDom { match node.dynamic_root(0) { None => node.root_ids[0].get(), Some(Text(t)) => t.id.get(), - Some(Fragment(VFragment::NonEmpty(t))) => self.find_first_element(&t[0]), - Some(Fragment(VFragment::Empty(t))) => t.get(), + Some(Fragment(t)) => self.find_first_element(&t[0]), + Some(Placeholder(t)) => t.get(), Some(Component(comp)) => { let scope = comp.scope.get().unwrap(); match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } { @@ -858,8 +821,8 @@ impl<'b> VirtualDom { match node.dynamic_root(node.template.roots.len() - 1) { None => node.root_ids.last().unwrap().get(), Some(Text(t)) => t.id.get(), - Some(Fragment(VFragment::NonEmpty(t))) => self.find_last_element(t.last().unwrap()), - Some(Fragment(VFragment::Empty(t))) => t.get(), + Some(Fragment(t)) => self.find_last_element(t.last().unwrap()), + Some(Placeholder(t)) => t.get(), Some(Component(comp)) => { let scope = comp.scope.get().unwrap(); match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } { diff --git a/packages/core/src/factory.rs b/packages/core/src/factory.rs index 20a98edea..0423d521e 100644 --- a/packages/core/src/factory.rs +++ b/packages/core/src/factory.rs @@ -10,7 +10,7 @@ use std::future::Future; use crate::{ any_props::{AnyProps, VProps}, arena::ElementId, - innerlude::{DynamicNode, EventHandler, VComponent, VFragment, VText}, + innerlude::{DynamicNode, EventHandler, VComponent, VText}, Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode, }; @@ -148,12 +148,20 @@ pub trait IntoDynNode<'a, A = ()> { impl<'a> IntoDynNode<'a> for () { fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { - DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))) + DynamicNode::placeholder() } } impl<'a> IntoDynNode<'a> for VNode<'a> { fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { - DynamicNode::Fragment(VFragment::NonEmpty(_cx.bump().alloc([self]))) + DynamicNode::Fragment(_cx.bump().alloc([self])) + } +} +impl<'a> IntoDynNode<'a> for Element<'a> { + fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { + match self { + Ok(val) => val.into_vnode(_cx), + _ => DynamicNode::placeholder(), + } } } @@ -161,7 +169,7 @@ impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option { fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { match self { Some(val) => val.into_vnode(_cx), - None => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))), + None => DynamicNode::placeholder(), } } } @@ -170,14 +178,14 @@ impl<'a> IntoDynNode<'a> for &Element<'a> { fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { match self.as_ref() { Ok(val) => val.clone().into_vnode(_cx), - _ => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))), + _ => DynamicNode::placeholder(), } } } impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> { fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> { - DynamicNode::Fragment(VFragment::NonEmpty(cx.bump().alloc([self.call(cx)]))) + DynamicNode::Fragment(cx.bump().alloc([self.call(cx)])) } } @@ -201,14 +209,14 @@ impl<'b> IntoDynNode<'b> for Arguments<'_> { impl<'a> IntoDynNode<'a> for &'a VNode<'a> { fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { - DynamicNode::Fragment(VFragment::NonEmpty(_cx.bump().alloc([VNode { + DynamicNode::Fragment(_cx.bump().alloc([VNode { parent: self.parent, template: self.template, root_ids: self.root_ids, key: self.key, dynamic_nodes: self.dynamic_nodes, dynamic_attrs: self.dynamic_attrs, - }]))) + }])) } } @@ -243,8 +251,8 @@ where let children = nodes.into_bump_slice(); match children.len() { - 0 => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))), - _ => DynamicNode::Fragment(VFragment::NonEmpty(children)), + 0 => DynamicNode::placeholder(), + _ => DynamicNode::Fragment(children), } } } diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index b81d8e9ed..8f9aa8f33 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -24,6 +24,7 @@ pub(crate) mod innerlude { pub use crate::dirty_scope::*; pub use crate::error_boundary::*; pub use crate::events::*; + pub use crate::factory::RenderReturn; pub use crate::fragment::*; pub use crate::lazynodes::*; pub use crate::mutations::*; @@ -87,6 +88,7 @@ pub use crate::innerlude::{ Mutations, NodeFactory, Properties, + RenderReturn, Scope, ScopeId, ScopeState, @@ -99,7 +101,6 @@ pub use crate::innerlude::{ TemplateNode, UiEvent, VComponent, - VFragment, VNode, VText, VirtualDom, diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs index 894dc28c8..70ed3684c 100644 --- a/packages/core/src/mutations.rs +++ b/packages/core/src/mutations.rs @@ -150,10 +150,6 @@ pub enum Mutation<'a> { id: ElementId, }, - SetInnerText { - value: &'a str, - }, - SetText { value: &'a str, id: ElementId, diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index e6dbcab2e..19955be64 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -89,13 +89,17 @@ pub enum TemplateNode<'a> { pub enum DynamicNode<'a> { Component(VComponent<'a>), Text(VText<'a>), - Fragment(VFragment<'a>), + Placeholder(Cell), + Fragment(&'a [VNode<'a>]), } impl<'a> DynamicNode<'a> { pub fn is_component(&self) -> bool { matches!(self, DynamicNode::Component(_)) } + pub fn placeholder() -> Self { + Self::Placeholder(Cell::new(ElementId(0))) + } } pub struct VComponent<'a> { @@ -122,12 +126,6 @@ pub struct VText<'a> { pub value: &'a str, } -#[derive(Debug)] -pub enum VFragment<'a> { - Empty(Cell), - NonEmpty(&'a [VNode<'a>]), -} - #[derive(Debug)] pub enum TemplateAttribute<'a> { Static { diff --git a/packages/core/src/scope_arena.rs b/packages/core/src/scope_arena.rs index 2419c52a0..62f6afa33 100644 --- a/packages/core/src/scope_arena.rs +++ b/packages/core/src/scope_arena.rs @@ -7,7 +7,7 @@ use crate::{ scheduler::RcWake, scopes::{ScopeId, ScopeState}, virtual_dom::VirtualDom, - AttributeValue, DynamicNode, VFragment, VNode, + AttributeValue, DynamicNode, VNode, }; use futures_util::FutureExt; use std::{ @@ -75,7 +75,7 @@ impl VirtualDom { c.props.set(None); } } - DynamicNode::Fragment(VFragment::NonEmpty(f)) => { + DynamicNode::Fragment(f) => { for node in *f { self.ensure_drop_safety_inner(node); } diff --git a/packages/desktop/src/controller.rs b/packages/desktop/src/controller.rs index 0f8492c58..3aff11352 100644 --- a/packages/desktop/src/controller.rs +++ b/packages/desktop/src/controller.rs @@ -67,7 +67,7 @@ impl DesktopController { let name = value.event.clone(); let el_id = ElementId(value.mounted_dom_id); if let Some(evt) = decode_event(value) { - dom.handle_event(&name, evt, el_id, true, EventPriority::Medium); + dom.handle_event(&name, evt, el_id, true); } } } diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index df9bed8db..bcfd77865 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -132,16 +132,13 @@ export class Interpreter { this.nodes[root] = node; this.stack.push(node); } - CreateElement(tag, root) { + CreateElement(tag) { const el = document.createElement(tag); - this.nodes[root] = el; this.stack.push(el); } - CreateElementNs(tag, root, ns) { - console.log("creating element", tag, root, ns); + CreateElementNs(tag, ns) { let el = document.createElementNS(ns, tag); this.stack.push(el); - this.nodes[root] = el; } CreatePlaceholder(root) { let el = document.createElement("pre"); @@ -149,6 +146,11 @@ export class Interpreter { this.stack.push(el); this.nodes[root] = el; } + CreateStaticPlaceholder() { + let el = document.createElement("pre"); + el.hidden = true; + this.stack.push(el); + } NewEventListener(event_name, root, handler, bubbles) { const element = this.nodes[root]; element.setAttribute("data-dioxus-id", `${root}`); @@ -162,9 +164,16 @@ export class Interpreter { SetText(root, text) { this.nodes[root].textContent = text; } - SetAttribute(root, field, value, ns) { + SetAttribute(id, field, value, ns) { + const node = this.nodes[id]; + this.SetAttributeInner(node, field, value, ns); + } + SetStaticAttribute(field, value, ns) { + const node = this.top(); + this.SetAttributeInner(node, field, value, ns); + } + SetAttributeInner(node, field, value, ns) { const name = field; - const node = this.nodes[root]; if (ns === "style") { // ????? why do we need to do this if (node.style === undefined) { @@ -248,9 +257,9 @@ export class Interpreter { let node = this.LoadChild(path); node.replaceWith(...els); } - LoadTemplate(name, index) { - console.log("loading template", name, index); + LoadTemplate(name, index, id) { let node = this.templates[name][index].cloneNode(true); + this.nodes[id] = node; this.stack.push(node); } SaveTemplate(name, m) { @@ -265,24 +274,38 @@ export class Interpreter { this.AssignId(edit.path, edit.id); break; case "CreateElement": - if (edit.namespace !== null && edit.namespace !== undefined) { - this.CreateElementNs(edit.name, edit.id, edit.namespace); - } else { - this.CreateElement(edit.name, edit.id); - } + this.CreateElement(edit.name); + break; + case "CreateElementNs": + this.CreateElementNs(edit.name, edit.namespace); break; case "CreatePlaceholder": this.CreatePlaceholder(edit.id); break; - case "CreateTextNode": + case "CreateStaticText": this.CreateTextNode(edit.value); break; + case "CreateStaticPlaceholder": + this.CreateStaticPlaceholder(); + break; + case "CreateTextPlaceholder": + this.CreateRawText("placeholder"); + break; case "CreateStaticText": + this.CreateRawText(edit.value); + break; + case "CreateTextNode": this.CreateTextNode(edit.value); break; case "HydrateText": this.HydrateText(edit.path, edit.value, edit.id); break; + case "LoadTemplate": + this.LoadTemplate(edit.name, edit.index); + break; + case "SaveTemplate": + this.SaveTemplate(edit.name, edit.m); + break; case "PushRoot": this.PushRoot(edit.id); break; @@ -293,29 +316,29 @@ export class Interpreter { this.ReplacePlaceholder(edit.path, edit.m); break; case "InsertAfter": - this.InsertAfter(edit.id, edit.n); + this.InsertAfter(edit.id, edit.m); break; case "InsertBefore": - this.InsertBefore(edit.id, edit.n); + this.InsertBefore(edit.id, edit.m); break; case "Remove": this.Remove(edit.id); break; - case "LoadTemplate": - this.LoadTemplate(edit.name, edit.index); - break; - case "SaveTemplate": - this.SaveTemplate(edit.name, edit.m); - break; - case "CreateElementNs": - this.CreateElementNs(edit.name, edit.id, edit.ns); - break; case "SetText": this.SetText(edit.id, edit.value); break; case "SetAttribute": this.SetAttribute(edit.id, edit.name, edit.value, edit.ns); break; + case "SetStaticAttribute": + this.SetStaticAttribute(edit.name, edit.value, edit.ns); + break; + case "SetBoolAttribute": + this.SetAttribute(edit.id, edit.name, edit.value, edit.ns); + break; + case "SetInnerText": + console.log("Set inner text?"); + break; case "RemoveAttribute": this.RemoveAttribute(edit.id, edit.name, edit.ns); break; diff --git a/packages/router/src/components/redirect.rs b/packages/router/src/components/redirect.rs index 561dec7b1..5bb0650e2 100644 --- a/packages/router/src/components/redirect.rs +++ b/packages/router/src/components/redirect.rs @@ -47,5 +47,5 @@ pub fn Redirect<'a>(cx: Scope<'a, RedirectProps<'a>>) -> Element { router.replace_route(cx.props.to, None, None); } - None + cx.render(rsx!(())) } diff --git a/packages/router/src/components/route.rs b/packages/router/src/components/route.rs index b613a4d5c..04b8944e1 100644 --- a/packages/router/src/components/route.rs +++ b/packages/router/src/components/route.rs @@ -29,7 +29,8 @@ pub struct RouteProps<'a> { pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element { let router_root = cx .use_hook(|| cx.consume_context::>()) - .as_ref()?; + .as_ref() + .unwrap(); cx.use_hook(|| { // create a bigger, better, longer route if one above us exists @@ -55,6 +56,6 @@ pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element { cx.render(rsx!(&cx.props.children)) } else { log::trace!("Route should *not* render: {:?}", cx.scope_id()); - None + cx.render(rsx!(())) } } diff --git a/packages/ssr/src/cache.rs b/packages/ssr/src/cache.rs index 0e7d585ff..d7c2d9ca0 100644 --- a/packages/ssr/src/cache.rs +++ b/packages/ssr/src/cache.rs @@ -3,6 +3,7 @@ use std::fmt::Write; pub struct StringCache { pub segments: Vec, + pub template: Template<'static>, } #[derive(Default)] @@ -40,6 +41,7 @@ impl StringCache { Ok(Self { segments: chain.segments, + template: template.template, }) } diff --git a/packages/ssr/src/template.rs b/packages/ssr/src/template.rs index 1df8c1581..d082a2f4a 100644 --- a/packages/ssr/src/template.rs +++ b/packages/ssr/src/template.rs @@ -1,5 +1,5 @@ use super::cache::Segment; -use dioxus_core::{prelude::*, AttributeValue, DynamicNode, VText}; +use dioxus_core::{prelude::*, AttributeValue, DynamicNode, RenderReturn, VText}; use std::collections::HashMap; use std::fmt::Write; use std::rc::Rc; @@ -9,7 +9,7 @@ use crate::cache::StringCache; /// A virtualdom renderer that caches the templates it has seen for faster rendering #[derive(Default)] pub struct SsrRender { - template_cache: HashMap, Rc>, + template_cache: HashMap<&'static str, Rc>, } impl SsrRender { @@ -18,7 +18,11 @@ impl SsrRender { let root = scope.root_node(); let mut out = String::new(); - // self.render_template(&mut out, dom, root).unwrap(); + + match root { + RenderReturn::Sync(Ok(node)) => self.render_template(&mut out, dom, node).unwrap(), + _ => {} + }; out } @@ -31,7 +35,7 @@ impl SsrRender { ) -> std::fmt::Result { let entry = self .template_cache - .entry(template.template) + .entry(template.template.id) .or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap())) .clone(); @@ -46,37 +50,38 @@ impl SsrRender { }; } Segment::Node(idx) => match &template.dynamic_nodes[*idx] { - DynamicNode::Component(_) => todo!(), - DynamicNode::Text(_) => todo!(), - DynamicNode::Fragment(_) => todo!(), - DynamicNode::Placeholder(_) => todo!(), - // todo!() - // DynamicNode::Text(VText { id, value }) => { - // // in SSR, we are concerned that we can't hunt down the right text node since they might get merged - // // if !*inner { - // write!(buf, "")?; - // // } + DynamicNode::Component(node) => { + let id = node.scope.get().unwrap(); + let scope = dom.get_scope(id).unwrap(); + let node = scope.root_node(); + match node { + RenderReturn::Sync(Ok(node)) => self.render_template(buf, dom, node)?, + _ => todo!(), + } + } + DynamicNode::Text(text) => { + // in SSR, we are concerned that we can't hunt down the right text node since they might get merged + // if !*inner { + // write!(buf, "")?; + // } - // // todo: escape the text - // write!(buf, "{}", value)?; + // todo: escape the text + write!(buf, "{}", text.value)?; - // // if !*inner { - // write!(buf, "")?; - // // } - // } - // DynamicNode::Fragment { nodes, .. } => { - // for child in *nodes { - // self.render_template(buf, dom, child)?; - // } - // } - // DynamicNode::Component { scope, .. } => { - // let id = scope.get().unwrap(); - // let scope = dom.get_scope(id).unwrap(); - // self.render_template(buf, dom, scope.root_node())?; - // } - // DynamicNode::Placeholder(_el) => { - // write!(buf, "")?; - // } + // if !*inner { + // write!(buf, "")?; + // } + } + DynamicNode::Fragment(nodes) => { + for child in *nodes { + self.render_template(buf, dom, child)?; + } + } + + DynamicNode::Placeholder(_el) => { + // todo write a placeholder if in pre-render mode + // write!(buf, "")?; + } }, Segment::PreRendered(contents) => buf.push_str(contents),