Browse Source

chore: get create working and simplify dynamic nodes

Jonathan Kelley 2 years ago
parent
commit
3c19def550

+ 2 - 2
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::<Query>()

+ 2 - 2
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!"
             }
 

+ 2 - 2
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);
                     }

+ 24 - 26
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<ElementId>,
         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(

+ 17 - 54
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<ElementId>, 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<ElementId>) {
-        // 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() } {

+ 18 - 10
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<T> {
     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),
         }
     }
 }

+ 2 - 1
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,

+ 0 - 4
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,

+ 5 - 7
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<ElementId>),
+    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<ElementId>),
-    NonEmpty(&'a [VNode<'a>]),
-}
-
 #[derive(Debug)]
 pub enum TemplateAttribute<'a> {
     Static {

+ 2 - 2
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);
                     }

+ 1 - 1
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);
                                 }
                             }
                         }

+ 49 - 26
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;

+ 1 - 1
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!(()))
 }

+ 3 - 2
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::<Arc<RouterCore>>())
-        .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!(()))
     }
 }

+ 2 - 0
packages/ssr/src/cache.rs

@@ -3,6 +3,7 @@ use std::fmt::Write;
 
 pub struct StringCache {
     pub segments: Vec<Segment>,
+    pub template: Template<'static>,
 }
 
 #[derive(Default)]
@@ -40,6 +41,7 @@ impl StringCache {
 
         Ok(Self {
             segments: chain.segments,
+            template: template.template,
         })
     }
 

+ 40 - 35
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<Template<'static>, Rc<StringCache>>,
+    template_cache: HashMap<&'static str, Rc<StringCache>>,
 }
 
 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, "<!--#-->")?;
-                    //     // }
-
-                    //     // todo: escape the text
-                    //     write!(buf, "{}", 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, "<!--placeholder-->")?;
-                    // }
+                    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, "{}", text.value)?;
+
+                        // 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, "<!--placeholder-->")?;
+                    }
                 },
 
                 Segment::PreRendered(contents) => buf.push_str(contents),