Pārlūkot izejas kodu

Merge pull request #41 from DioxusLabs/jk/combine_scope

Concept: combine scope and props together into a single Context<Props>
Jonathan Kelley 3 gadi atpakaļ
vecāks
revīzija
97e014fb46
38 mainītis faili ar 404 papildinājumiem un 497 dzēšanām
  1. 4 4
      examples/borrowed.rs
  2. 1 1
      examples/calculator.rs
  3. 1 1
      examples/coroutine.rs
  4. 1 1
      examples/framework_benchmark.rs
  5. 1 1
      examples/hello_world.rs
  6. 1 1
      examples/pattern_model.rs
  7. 3 3
      examples/rsx_usage.rs
  8. 1 1
      examples/ssr.rs
  9. 1 1
      examples/todomvc.rs
  10. 1 1
      examples/web_tick.rs
  11. 1 1
      packages/core/benches/jsframework.rs
  12. 51 0
      packages/core/examples/borrowed.rs
  13. 2 6
      packages/core/examples/handler_thru_props.rs
  14. 16 0
      packages/core/examples/hooks.rs
  15. 4 4
      packages/core/examples/rows.rs
  16. 5 5
      packages/core/examples/works.rs
  17. 16 12
      packages/core/src/component.rs
  18. 23 72
      packages/core/src/diff.rs
  19. 9 9
      packages/core/src/lib.rs
  20. 19 122
      packages/core/src/nodes.rs
  21. 38 31
      packages/core/src/scope.rs
  22. 51 85
      packages/core/src/scopearena.rs
  23. 44 30
      packages/core/src/virtual_dom.rs
  24. 5 5
      packages/core/tests/borrowedstate.rs
  25. 11 11
      packages/core/tests/create_dom.rs
  26. 1 1
      packages/core/tests/diffing.rs
  27. 20 20
      packages/core/tests/lifecycle.rs
  28. 2 2
      packages/core/tests/sharedstate.rs
  29. 6 6
      packages/core/tests/vdom_rebuild.rs
  30. 4 4
      packages/hooks/src/use_shared_state.rs
  31. 4 5
      packages/hooks/src/usecoroutine.rs
  32. 7 7
      packages/hooks/src/usemodel.rs
  33. 2 2
      packages/hooks/src/useref.rs
  34. 23 14
      packages/hooks/src/usestate.rs
  35. 8 8
      packages/hooks/src/usestate2.rs
  36. 9 9
      packages/router/src/lib.rs
  37. 7 10
      packages/ssr/src/lib.rs
  38. 1 1
      packages/web/examples/js_bench.rs

+ 4 - 4
examples/borrowed.rs

@@ -20,7 +20,7 @@ fn main() {
     dioxus::desktop::launch(App, |c| c);
 }
 
-fn App(cx: Context, props: &()) -> Element {
+fn App(cx: Scope, props: &()) -> Element {
     let text: &mut Vec<String> = cx.use_hook(|_| vec![String::from("abc=def")], |f| f);
 
     let first = text.get_mut(0).unwrap();
@@ -39,7 +39,7 @@ struct C1Props<'a> {
     text: &'a mut String,
 }
 
-fn Child1(cx: Context, props: &C1Props) -> Element {
+fn Child1(cx: Scope, props: &C1Props) -> Element {
     let (left, right) = props.text.split_once("=").unwrap();
 
     cx.render(rsx! {
@@ -55,7 +55,7 @@ struct C2Props<'a> {
     text: &'a str,
 }
 
-fn Child2(cx: Context, props: &C2Props) -> Element {
+fn Child2(cx: Scope, props: &C2Props) -> Element {
     cx.render(rsx! {
         Child3 {
             text: props.text
@@ -68,7 +68,7 @@ struct C3Props<'a> {
     text: &'a str,
 }
 
-fn Child3(cx: Context, props: &C3Props) -> Element {
+fn Child3(cx: Scope, props: &C3Props) -> Element {
     cx.render(rsx! {
         div { "{props.text}"}
     })

+ 1 - 1
examples/calculator.rs

@@ -120,7 +120,7 @@ struct CalculatorKeyProps<'a> {
     children: Element,
 }
 
-fn CalculatorKey<'a>(cx: Context, props: &CalculatorKeyProps) -> Element {
+fn CalculatorKey<'a>(cx: Scope, props: &CalculatorKeyProps) -> Element {
     rsx!(cx, button {
         class: "calculator-key {props.name}"
         onclick: {props.onclick}

+ 1 - 1
examples/coroutine.rs

@@ -61,7 +61,7 @@ struct HorseyProps<'a> {
     children: ScopeChildren<'a>,
 }
 
-fn Horsey<'a>((cx, props): Scope<'a, HorseyProps<'a>>) -> Element {
+fn Horsey<'a>((cx, props): ScopeState<'a, HorseyProps<'a>>) -> Element {
     cx.render(rsx! {
         div {
             button { "pause" }

+ 1 - 1
examples/framework_benchmark.rs

@@ -95,7 +95,7 @@ struct ActionButtonProps<'a> {
     onclick: &'a dyn Fn(),
 }
 
-fn ActionButton(cx: Context, props: &ActionButtonProps) -> Element {
+fn ActionButton(cx: Scope, props: &ActionButtonProps) -> Element {
     rsx!(cx, div { class: "col-sm-6 smallpad"
         button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}",  onclick: move |_| (props.onclick)(),
             "{props.name}"

+ 1 - 1
examples/hello_world.rs

@@ -4,7 +4,7 @@ fn main() {
     dioxus::desktop::launch(App, |c| c);
 }
 
-fn App((cx, props): Scope<()>) -> Element {
+fn App((cx, props): ScopeState<()>) -> Element {
     cx.render(rsx! (
         div { "Hello, world!" }
     ))

+ 1 - 1
examples/pattern_model.rs

@@ -77,7 +77,7 @@ struct CalculatorKeyProps<'a> {
     children: ScopeChildren<'a>,
 }
 
-fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps<'a>>) -> Element<'a> {
+fn CalculatorKey<'a>((cx, props): ScopeState<'a, CalculatorKeyProps<'a>>) -> Element<'a> {
     cx.render(rsx! {
         button {
             class: "calculator-key {props.name}"

+ 3 - 3
examples/rsx_usage.rs

@@ -178,7 +178,7 @@ pub static Example: Component<()> = |cx, props| {
     })
 };
 
-fn helper(cx: Context, text: &str) -> Element {
+fn helper(cx: Scope, text: &str) -> Element {
     rsx!(cx, p { "{text}" })
 }
 
@@ -188,7 +188,7 @@ mod baller {
     pub struct BallerProps {}
 
     /// This component totally balls
-    pub fn Baller(_: Scope<BallerProps>) -> Element {
+    pub fn Baller(_: ScopeState<BallerProps>) -> Element {
         todo!()
     }
 }
@@ -202,7 +202,7 @@ pub struct TallerProps<'a> {
 }
 
 /// This component is taller than most :)
-pub fn Taller<'a>(_: Scope<'a, TallerProps<'a>>) -> Element {
+pub fn Taller<'a>(_: ScopeState<'a, TallerProps<'a>>) -> Element {
     let b = true;
     todo!()
 }

+ 1 - 1
examples/ssr.rs

@@ -21,6 +21,6 @@ static App: Component<()> = |cx, props| {
 struct MyProps<'a> {
     text: &'a str,
 }
-fn App2(cx: Context, props: &MyProps) -> Element {
+fn App2(cx: Scope, props: &MyProps) -> Element {
     None
 }

+ 1 - 1
examples/todomvc.rs

@@ -85,7 +85,7 @@ pub struct TodoEntryProps {
     todo: Rc<TodoItem>,
 }
 
-pub fn TodoEntry(cx: Context, props: &TodoEntryProps) -> Element {
+pub fn TodoEntry(cx: Scope, props: &TodoEntryProps) -> Element {
     let mut is_editing = use_state(cx, || false);
     let mut contents = use_state(cx, || String::from(""));
     let todo = &props.todo;

+ 1 - 1
examples/web_tick.rs

@@ -45,7 +45,7 @@ struct RowProps {
     row_id: usize,
     label: Label,
 }
-fn Row((cx, props): Scope<RowProps>) -> Element {
+fn Row((cx, props): ScopeState<RowProps>) -> Element {
     let [adj, col, noun] = props.label.0;
     cx.render(rsx! {
         tr {

+ 1 - 1
packages/core/benches/jsframework.rs

@@ -53,7 +53,7 @@ struct RowProps {
     row_id: usize,
     label: Label,
 }
-fn Row(cx: Context, props: &RowProps) -> Element {
+fn Row(cx: Scope, props: &RowProps) -> Element {
     let [adj, col, noun] = props.label.0;
     cx.render(rsx! {
         tr {

+ 51 - 0
packages/core/examples/borrowed.rs

@@ -0,0 +1,51 @@
+use dioxus::prelude::*;
+use dioxus_core as dioxus;
+use dioxus_core_macro::*;
+use dioxus_html as dioxus_elements;
+
+fn main() {}
+
+fn App(cx: Scope<()>) -> Element {
+    cx.render(rsx!(div {
+        App2 {
+            p: "asd"
+        }
+    }))
+}
+
+#[derive(Props)]
+struct Borrowed<'a> {
+    p: &'a str,
+}
+
+fn App2<'a>(cx: Scope<'a, Borrowed<'a>>) -> Element {
+    let g = eat2(&cx);
+    todo!()
+}
+
+fn eat2(s: &ScopeState) {}
+
+fn eat(f: &str) {}
+
+fn bleat() {
+    let blah = String::from("asd");
+    eat(&blah);
+}
+
+// struct Lower {}
+
+// #[derive(Clone, Copy)]
+// struct Upper {}
+// impl std::ops::Deref for Upper {
+//     type Target = Lower;
+
+//     fn deref(&self) -> &Self::Target {
+//         todo!()
+//     }
+// }
+
+// fn mark(f: &Lower) {}
+// fn bark() {
+//     let up = Upper {};
+//     mark(&up);
+// }

+ 2 - 6
packages/core/examples/handler_thru_props.rs

@@ -9,7 +9,7 @@ fn main() {
     let _ = VirtualDom::new(App);
 }
 
-fn App(cx: Context, _props: &()) -> Element {
+fn App(cx: Scope<()>) -> Element {
     //
     cx.render(rsx!(
         div {
@@ -18,11 +18,7 @@ fn App(cx: Context, _props: &()) -> Element {
     ))
 }
 
-struct ChildProps<'a> {
-    click_handler: EventHandler<'a>,
-}
-
-fn Child(cx: Context, _props: &()) -> Element {
+fn Child(cx: Scope<()>) -> Element {
     //
     cx.render(rsx!(
         div {

+ 16 - 0
packages/core/examples/hooks.rs

@@ -0,0 +1,16 @@
+use dioxus::prelude::*;
+use dioxus_core as dioxus;
+use dioxus_core_macro::*;
+use dioxus_hooks::use_state;
+use dioxus_html as dioxus_elements;
+
+fn main() {}
+
+fn App(cx: Scope<()>) -> Element {
+    let color = use_state(&cx, || "white");
+
+    cx.render(rsx!(
+        div { onclick: move |_| color.set("red"), "red" }
+        div { onclick: move |_| color.set("blue"), "blue" }
+    ))
+}

+ 4 - 4
packages/core/examples/rows.rs

@@ -20,7 +20,7 @@ use dioxus_html as dioxus_elements;
 use rand::prelude::*;
 
 fn main() {
-    static App: Component<()> = |cx, _| {
+    static App: Component<()> = |cx| {
         let mut rng = SmallRng::from_entropy();
         let rows = (0..10_000_usize).map(|f| {
             let label = Label::new(&mut rng);
@@ -50,11 +50,11 @@ struct RowProps {
     row_id: usize,
     label: Label,
 }
-fn Row(cx: Context, props: &RowProps) -> Element {
-    let [adj, col, noun] = props.label.0;
+fn Row(cx: Scope<RowProps>) -> Element {
+    let [adj, col, noun] = cx.props.label.0;
     cx.render(rsx! {
         tr {
-            td { class:"col-md-1", "{props.row_id}" }
+            td { class:"col-md-1", "{cx.props.row_id}" }
             td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
                 a { class: "lbl", "{adj}" "{col}" "{noun}" }
             }

+ 5 - 5
packages/core/examples/works.rs

@@ -9,7 +9,7 @@ fn main() {
     let _ = VirtualDom::new(Parent);
 }
 
-fn Parent(cx: Context, _props: &()) -> Element {
+fn Parent(cx: Scope<()>) -> Element {
     let value = cx.use_hook(|_| String::new(), |f| f);
 
     cx.render(rsx! {
@@ -25,11 +25,11 @@ struct ChildProps<'a> {
     name: &'a str,
 }
 
-fn Child(cx: Context, props: &ChildProps) -> Element {
+fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
     cx.render(rsx! {
         div {
             h1 { "it's nested" }
-            Child2 { name: props.name }
+            Child2 { name: cx.props.name }
         }
     })
 }
@@ -39,8 +39,8 @@ struct Grandchild<'a> {
     name: &'a str,
 }
 
-fn Child2(cx: Context, props: &Grandchild) -> Element {
+fn Child2<'a>(cx: Scope<'a, Grandchild<'a>>) -> Element {
     cx.render(rsx! {
-        div { "Hello {props.name}!" }
+        div { "Hello {cx.props.name}!" }
     })
 }

+ 16 - 12
packages/core/src/component.rs

@@ -5,17 +5,20 @@
 //! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
 //! that ensures compile-time required and optional fields on cx.
 
-use crate::innerlude::{Context, Element, LazyNodes, VPortal};
+use crate::{
+    innerlude::{Element, Scope},
+    LazyNodes,
+};
 
-pub struct FragmentProps(Element);
-pub struct FragmentBuilder<const BUILT: bool>(Element);
-impl FragmentBuilder<false> {
-    pub fn children(self, children: Option<VPortal>) -> FragmentBuilder<true> {
+pub struct FragmentProps<'a>(Element<'a>);
+pub struct FragmentBuilder<'a, const BUILT: bool>(Element<'a>);
+impl<'a> FragmentBuilder<'a, false> {
+    pub fn children(self, children: Element<'a>) -> FragmentBuilder<'a, true> {
         FragmentBuilder(children)
     }
 }
-impl<const A: bool> FragmentBuilder<A> {
-    pub fn build(self) -> FragmentProps {
+impl<'a, const A: bool> FragmentBuilder<'a, A> {
+    pub fn build(self) -> FragmentProps<'a> {
         FragmentProps(self.0)
     }
 }
@@ -60,8 +63,8 @@ impl<const A: bool> FragmentBuilder<A> {
 ///     })
 /// }
 /// ```
-impl Properties for FragmentProps {
-    type Builder = FragmentBuilder<false>;
+impl<'a> Properties for FragmentProps<'a> {
+    type Builder = FragmentBuilder<'a, false>;
     const IS_STATIC: bool = false;
     fn builder() -> Self::Builder {
         FragmentBuilder(None)
@@ -97,8 +100,9 @@ impl Properties for FragmentProps {
 ///
 /// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
 #[allow(non_upper_case_globals, non_snake_case)]
-pub fn Fragment(cx: Context, props: &FragmentProps) -> Element {
-    cx.render(Some(LazyNodes::new(|f| f.fragment_from_iter(&props.0))))
+pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
+    let i = cx.props.0.as_ref().map(|f| f.decouple());
+    cx.render(Some(LazyNodes::new(|f| f.fragment_from_iter(i))))
 }
 
 /// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
@@ -165,6 +169,6 @@ impl EmptyBuilder {
 
 /// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
 /// to initialize a component's props.
-pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Context<'a>, &'a T) -> Element) -> T::Builder {
+pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
     T::builder()
 }

+ 23 - 72
packages/core/src/diff.rs

@@ -239,10 +239,10 @@ impl<'bump> DiffStack<'bump> {
 }
 
 impl<'bump> DiffState<'bump> {
-    pub fn diff_scope(&mut self, id: &ScopeId) {
+    pub fn diff_scope(&mut self, id: ScopeId) {
         let (old, new) = (self.scopes.wip_head(id), self.scopes.fin_head(id));
         self.stack.push(DiffInstruction::Diff { old, new });
-        self.stack.scope_stack.push(*id);
+        self.stack.scope_stack.push(id);
         let scope = self.scopes.get_scope(id).unwrap();
         self.stack.element_stack.push(scope.container);
         self.work(|| false);
@@ -288,12 +288,6 @@ impl<'bump> DiffState<'bump> {
                 1
             }
 
-            VNode::Portal(linked) => {
-                let node = unsafe { &*linked.node };
-                let node: &VNode = unsafe { std::mem::transmute(node) };
-                self.push_all_nodes(node)
-            }
-
             VNode::Fragment(_) | VNode::Component(_) => {
                 //
                 let mut added = 0;
@@ -357,7 +351,6 @@ impl<'bump> DiffState<'bump> {
             VNode::Element(element) => self.create_element_node(element, node),
             VNode::Fragment(frag) => self.create_fragment_node(frag),
             VNode::Component(component) => self.create_component_node(*component),
-            VNode::Portal(linked) => self.create_linked_node(linked),
         }
     }
 
@@ -406,7 +399,7 @@ impl<'bump> DiffState<'bump> {
         self.stack.add_child_count(1);
 
         if let Some(cur_scope_id) = self.stack.current_scope() {
-            let scope = self.scopes.get_scope(&cur_scope_id).unwrap();
+            let scope = self.scopes.get_scope(cur_scope_id).unwrap();
 
             for listener in *listeners {
                 self.attach_listener_to_scope(listener, scope);
@@ -443,11 +436,11 @@ impl<'bump> DiffState<'bump> {
         let parent_idx = self.stack.current_scope().unwrap();
 
         // Insert a new scope into our component list
-        let parent_scope = self.scopes.get_scope(&parent_idx).unwrap();
+        let parent_scope = self.scopes.get_scope(parent_idx).unwrap();
         let height = parent_scope.height + 1;
         let subtree = parent_scope.subtree.get();
 
-        let parent_scope = unsafe { self.scopes.get_scope_raw(&parent_idx) };
+        let parent_scope = unsafe { self.scopes.get_scope_raw(parent_idx) };
         let caller = unsafe { std::mem::transmute(vcomponent.caller as *const _) };
         let fc_ptr = vcomponent.user_fc;
 
@@ -461,7 +454,7 @@ impl<'bump> DiffState<'bump> {
         vcomponent.associated_scope.set(Some(new_idx));
 
         if !vcomponent.can_memoize {
-            let cur_scope = self.scopes.get_scope(&parent_idx).unwrap();
+            let cur_scope = self.scopes.get_scope(parent_idx).unwrap();
             let extended = unsafe { std::mem::transmute(vcomponent) };
             cur_scope.items.borrow_mut().borrowed_props.push(extended);
         } else {
@@ -469,7 +462,7 @@ impl<'bump> DiffState<'bump> {
         }
 
         // TODO: add noderefs to current noderef list Noderefs
-        let _new_component = self.scopes.get_scope(&new_idx).unwrap();
+        let _new_component = self.scopes.get_scope(new_idx).unwrap();
 
         log::debug!(
             "initializing component {:?} with height {:?}",
@@ -478,9 +471,9 @@ impl<'bump> DiffState<'bump> {
         );
 
         // Run the scope for one iteration to initialize it
-        if self.scopes.run_scope(&new_idx) {
+        if self.scopes.run_scope(new_idx) {
             // Take the node that was just generated from running the component
-            let nextnode = self.scopes.fin_head(&new_idx);
+            let nextnode = self.scopes.fin_head(new_idx);
             self.stack.create_component(new_idx, nextnode);
 
             // todo: subtrees
@@ -493,16 +486,6 @@ impl<'bump> DiffState<'bump> {
         self.mutations.dirty_scopes.insert(new_idx);
     }
 
-    fn create_linked_node(&mut self, link: &'bump VPortal) {
-        if link.scope_id.get().is_none() {
-            if let Some(cur_scope) = self.stack.current_scope() {
-                link.scope_id.set(Some(cur_scope));
-            }
-        }
-        let node: &'bump VNode<'static> = unsafe { &*link.node };
-        self.create_node(unsafe { std::mem::transmute(node) });
-    }
-
     // =================================
     //  Tools for diffing nodes
     // =================================
@@ -520,12 +503,11 @@ impl<'bump> DiffState<'bump> {
             (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
             (Placeholder(old), Placeholder(new)) => new.dom_id.set(old.dom_id.get()),
             (Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
-            (Portal(old), Portal(new)) => self.diff_linked_nodes(old, new),
 
             // Anything else is just a basic replace and create
             (
-                Portal(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
-                Portal(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
+                Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
+                Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
             ) => self
                 .stack
                 .create_node(new_node, MountType::Replace { old: old_node }),
@@ -613,7 +595,7 @@ impl<'bump> DiffState<'bump> {
         //
         // TODO: take a more efficient path than this
         if let Some(cur_scope_id) = self.stack.current_scope() {
-            let scope = self.scopes.get_scope(&cur_scope_id).unwrap();
+            let scope = self.scopes.get_scope(cur_scope_id).unwrap();
 
             if old.listeners.len() == new.listeners.len() {
                 for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
@@ -736,7 +718,7 @@ impl<'bump> DiffState<'bump> {
             // make sure the component's caller function is up to date
             let scope = unsafe {
                 self.scopes
-                    .get_scope_mut(&scope_addr)
+                    .get_scope_mut(scope_addr)
                     .unwrap_or_else(|| panic!("could not find {:?}", scope_addr))
             };
 
@@ -745,10 +727,10 @@ impl<'bump> DiffState<'bump> {
             // React doesn't automatically memoize, but we do.
             let props_are_the_same = old.comparator.unwrap();
 
-            if (self.force_diff || !props_are_the_same(new)) && self.scopes.run_scope(&scope_addr) {
+            if (self.force_diff || !props_are_the_same(new)) && self.scopes.run_scope(scope_addr) {
                 self.diff_node(
-                    self.scopes.wip_head(&scope_addr),
-                    self.scopes.fin_head(&scope_addr),
+                    self.scopes.wip_head(scope_addr),
+                    self.scopes.fin_head(scope_addr),
                 );
             }
 
@@ -773,19 +755,6 @@ impl<'bump> DiffState<'bump> {
         self.diff_children(old.children, new.children);
     }
 
-    fn diff_linked_nodes(&mut self, old: &'bump VPortal, new: &'bump VPortal) {
-        if !std::ptr::eq(old.node, new.node) {
-            // if the ptrs are the same then theyr're the same
-            let old: &VNode = unsafe { std::mem::transmute(&*old.node) };
-            let new: &VNode = unsafe { std::mem::transmute(&*new.node) };
-            self.diff_node(old, new);
-        }
-
-        if new.scope_id.get().is_none() {
-            todo!("attach the link to the scope - when children are not created");
-        }
-    }
-
     // =============================================
     //  Utilities for creating new diff instructions
     // =============================================
@@ -1226,16 +1195,12 @@ impl<'bump> DiffState<'bump> {
                 VNode::Text(t) => break t.dom_id.get(),
                 VNode::Element(t) => break t.dom_id.get(),
                 VNode::Placeholder(t) => break t.dom_id.get(),
-                VNode::Portal(l) => {
-                    let node: &VNode = unsafe { std::mem::transmute(&*l.node) };
-                    self.find_last_element(node);
-                }
                 VNode::Fragment(frag) => {
                     search_node = frag.children.last();
                 }
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
-                    search_node = Some(self.scopes.root_node(&scope_id));
+                    search_node = Some(self.scopes.root_node(scope_id));
                 }
             }
         }
@@ -1252,11 +1217,7 @@ impl<'bump> DiffState<'bump> {
                 }
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
-                    search_node = Some(self.scopes.root_node(&scope_id));
-                }
-                VNode::Portal(link) => {
-                    let node = unsafe { std::mem::transmute(&*link.node) };
-                    search_node = Some(node);
+                    search_node = Some(self.scopes.root_node(scope_id));
                 }
                 VNode::Text(t) => break t.dom_id.get(),
                 VNode::Element(t) => break t.dom_id.get(),
@@ -1292,17 +1253,12 @@ impl<'bump> DiffState<'bump> {
             }
 
             VNode::Component(c) => {
-                let node = self.scopes.fin_head(&c.associated_scope.get().unwrap());
+                let node = self.scopes.fin_head(c.associated_scope.get().unwrap());
                 self.replace_node(node, nodes_created);
 
                 let scope_id = c.associated_scope.get().unwrap();
                 log::debug!("Destroying scope {:?}", scope_id);
-                self.scopes.try_remove(&scope_id).unwrap();
-            }
-
-            VNode::Portal(l) => {
-                let node: &'bump VNode<'bump> = unsafe { std::mem::transmute(&*l.node) };
-                self.replace_node(node, nodes_created);
+                self.scopes.try_remove(scope_id).unwrap();
             }
         }
     }
@@ -1349,11 +1305,6 @@ impl<'bump> DiffState<'bump> {
                     self.remove_nodes(f.children, gen_muts);
                 }
 
-                VNode::Portal(l) => {
-                    let node = unsafe { std::mem::transmute(&*l.node) };
-                    self.remove_nodes(Some(node), gen_muts);
-                }
-
                 VNode::Component(c) => {
                     self.destroy_vomponent(c, gen_muts);
                 }
@@ -1363,14 +1314,14 @@ impl<'bump> DiffState<'bump> {
 
     fn destroy_vomponent(&mut self, vc: &VComponent, gen_muts: bool) {
         let scope_id = vc.associated_scope.get().unwrap();
-        let root = self.scopes.root_node(&scope_id);
+        let root = self.scopes.root_node(scope_id);
         self.remove_nodes(Some(root), gen_muts);
         log::debug!("Destroying scope {:?}", scope_id);
-        self.scopes.try_remove(&scope_id).unwrap();
+        self.scopes.try_remove(scope_id).unwrap();
     }
 
     /// Adds a listener closure to a scope during diff.
-    fn attach_listener_to_scope(&mut self, listener: &'bump Listener<'bump>, scope: &Scope) {
+    fn attach_listener_to_scope(&mut self, listener: &'bump Listener<'bump>, scope: &ScopeState) {
         let long_listener = unsafe { std::mem::transmute(listener) };
         scope.items.borrow_mut().listeners.push(long_listener)
     }

+ 9 - 9
packages/core/src/lib.rs

@@ -17,24 +17,24 @@ pub(crate) mod innerlude {
     pub use crate::mutations::*;
     pub use crate::nodes::*;
     pub use crate::scope::*;
-    pub use crate::scopearena::*;
+    pub(crate) use crate::scopearena::*;
     pub use crate::virtual_dom::*;
 
-    pub type Element = Option<VPortal>;
-    pub type Component<P> = for<'a> fn(Context<'a>, &'a P) -> Element;
+    pub type Element<'a> = Option<VNode<'a>>;
+    pub type Component<P> = for<'a> fn(Scope<'a, P>) -> Element<'a>;
 }
 
 pub use crate::innerlude::{
-    Attribute, Component, Context, DioxusElement, DomEdit, Element, ElementId, EventHandler,
-    EventPriority, IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties,
-    SchedulerMsg, ScopeId, UserEvent, VElement, VFragment, VNode, VirtualDom,
+    Attribute, Component, DioxusElement, DomEdit, Element, ElementId, EventHandler, EventPriority,
+    IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties, SchedulerMsg, Scope,
+    ScopeId, ScopeState, UserEvent, VElement, VFragment, VNode, VirtualDom,
 };
 
 pub mod prelude {
     pub use crate::component::{fc_to_builder, Fragment, Properties};
-    pub use crate::innerlude::Context;
+    pub use crate::innerlude::Scope;
     pub use crate::innerlude::{
-        Component, DioxusElement, Element, EventHandler, LazyNodes, NodeFactory, Scope,
+        Component, DioxusElement, Element, EventHandler, LazyNodes, NodeFactory, ScopeState,
     };
     pub use crate::nodes::VNode;
     pub use crate::VirtualDom;
@@ -42,7 +42,7 @@ pub mod prelude {
 
 pub mod exports {
     //! Important dependencies that are used by the rest of the library
-    // the foundation of this library
+    //! Feel free to just add the dependencies in your own Crates.toml
     pub use bumpalo;
     pub use futures_channel;
 }

+ 19 - 122
packages/core/src/nodes.rs

@@ -4,7 +4,7 @@
 //! cheap and *very* fast to construct - building a full tree should be quick.
 
 use crate::{
-    innerlude::{Context, Element, Properties, Scope, ScopeId},
+    innerlude::{Element, Properties, Scope, ScopeId, ScopeState},
     lazynodes::LazyNodes,
 };
 use bumpalo::{boxed::Box as BumpBox, Bump};
@@ -114,22 +114,6 @@ pub enum VNode<'src> {
     /// }
     /// ```
     Placeholder(&'src VPlaceholder),
-
-    /// A VNode that is actually a pointer to some nodes rather than the nodes directly. Useful when rendering portals
-    /// or eliding lifetimes on VNodes through runtime checks.
-    ///
-    /// Linked VNodes can only be made through the [`Context::render`] method
-    ///
-    /// Typically, linked nodes are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
-    /// an `rsx!` call.
-    ///
-    /// # Example
-    /// ```rust, ignore
-    /// let mut vdom = VirtualDom::new();
-    ///
-    /// let node: NodeLink = vdom.render_vnode(rsx!( "hello" ));
-    /// ```
-    Portal(VPortal),
 }
 
 impl<'src> VNode<'src> {
@@ -141,7 +125,6 @@ impl<'src> VNode<'src> {
             VNode::Fragment(f) => f.key,
             VNode::Text(_t) => None,
             VNode::Placeholder(_f) => None,
-            VNode::Portal(_c) => None,
         }
     }
 
@@ -160,7 +143,6 @@ impl<'src> VNode<'src> {
             VNode::Text(el) => el.dom_id.get(),
             VNode::Element(el) => el.dom_id.get(),
             VNode::Placeholder(el) => el.dom_id.get(),
-            VNode::Portal(_) => None,
             VNode::Fragment(_) => None,
             VNode::Component(_) => None,
         }
@@ -184,11 +166,6 @@ impl<'src> VNode<'src> {
                 children: f.children,
                 key: f.key,
             }),
-            VNode::Portal(c) => VNode::Portal(VPortal {
-                scope_id: c.scope_id.clone(),
-                link_idx: c.link_idx.clone(),
-                node: c.node,
-            }),
         }
     }
 }
@@ -207,7 +184,6 @@ impl Debug for VNode<'_> {
                 write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
             }
             VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
-            VNode::Portal(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
         }
     }
 }
@@ -341,6 +317,7 @@ pub struct Listener<'bump> {
     pub(crate) callback: EventHandler<'bump>,
 }
 
+/// The callback based into element event listeners.
 pub struct EventHandler<'bump> {
     pub callback: &'bump RefCell<Option<ListenerCallback<'bump>>>,
 }
@@ -355,6 +332,7 @@ impl EventHandler<'_> {
         self.callback.replace(None);
     }
 }
+
 type ListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(Arc<dyn Any + Send + Sync>) + 'bump>;
 
 impl Copy for EventHandler<'_> {}
@@ -366,32 +344,6 @@ impl Clone for EventHandler<'_> {
     }
 }
 
-/// A cached node is a "pointer" to a "rendered" node in a particular scope
-///
-/// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
-///
-/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
-/// is essentially a unique key to guarantee safe usage of the Node.
-///
-/// Linked VNodes can only be made through the [`Context::render`] method
-///
-/// Typically, NodeLinks are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
-/// an `rsx!` call.
-///
-/// todo: remove the raw pointer and use runtime checks instead
-#[derive(Debug)]
-pub struct VPortal {
-    pub(crate) link_idx: Cell<usize>,
-    pub(crate) scope_id: Cell<Option<ScopeId>>,
-    pub(crate) node: *const VNode<'static>,
-}
-
-impl PartialEq for VPortal {
-    fn eq(&self, other: &Self) -> bool {
-        self.node == other.node
-    }
-}
-
 /// Virtual Components for custom user-defined components
 /// Only supports the functional syntax
 pub struct VComponent<'src> {
@@ -410,7 +362,7 @@ pub struct VComponent<'src> {
     pub(crate) bump_props: *const (),
 
     // during the "teardown" process we'll take the caller out so it can be dropped properly
-    pub(crate) caller: &'src dyn Fn(&'src Scope) -> Element,
+    pub(crate) caller: &'src dyn Fn(&'src ScopeState) -> Element,
 
     pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
 
@@ -552,7 +504,7 @@ impl<'a> NodeFactory<'a> {
 
     pub fn component<P>(
         &self,
-        component: fn(Context<'a>, &'a P) -> Element,
+        component: fn(Scope<'a, P>) -> Element,
         props: P,
         key: Option<Arguments>,
     ) -> VNode<'a>
@@ -610,10 +562,10 @@ impl<'a> NodeFactory<'a> {
 
         let key = key.map(|f| self.raw_text(f).0);
 
-        let caller: &'a mut dyn Fn(&'a Scope) -> Element =
-            bump.alloc(move |scope: &Scope| -> Element {
+        let caller: &'a mut dyn Fn(&'a ScopeState) -> Element =
+            bump.alloc(move |scope: &ScopeState| -> Element {
                 let props: &'_ P = unsafe { &*(bump_props as *const P) };
-                component(scope, props)
+                component(Scope { scope, props })
             });
 
         let can_memoize = P::IS_STATIC;
@@ -705,7 +657,7 @@ impl<'a> NodeFactory<'a> {
     pub fn create_children(
         self,
         node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
-    ) -> Element {
+    ) -> Element<'a> {
         let bump = self.bump;
         let mut nodes = bumpalo::collections::Vec::new_in(bump);
 
@@ -723,33 +675,11 @@ impl<'a> NodeFactory<'a> {
 
         // TODO
         // We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
-        //
-        // if cfg!(debug_assertions) {
-        //     if children.len() > 1 {
-        //         if children.last().unwrap().key().is_none() {
-        //             log::error!(
-        //                 r#"
-        // Warning: Each child in an array or iterator should have a unique "key" prop.
-        // Not providing a key will lead to poor performance with lists.
-        // See docs.rs/dioxus for more information.
-        // ---
-        // To help you identify where this error is coming from, we've generated a backtrace.
-        //                         "#,
-        //             );
-        //         }
-        //     }
-        // }
-
-        let frag = VNode::Fragment(VFragment {
+
+        Some(VNode::Fragment(VFragment {
             children,
             key: None,
-        });
-        let ptr = self.bump.alloc(frag) as *const _;
-        Some(VPortal {
-            link_idx: Default::default(),
-            scope_id: Default::default(),
-            node: unsafe { std::mem::transmute(ptr) },
-        })
+        }))
     }
 }
 
@@ -845,48 +775,15 @@ impl IntoVNode<'_> for Arguments<'_> {
     }
 }
 
-// called cx.render from a helper function
-impl IntoVNode<'_> for Option<VPortal> {
-    fn into_vnode(self, _cx: NodeFactory) -> VNode {
-        match self {
-            Some(node) => VNode::Portal(node),
-            None => {
-                todo!()
-            }
-        }
-    }
-}
-
-// essentially passing elements through props
-// just build a new element in place
-impl IntoVNode<'_> for &Option<VPortal> {
-    fn into_vnode(self, _cx: NodeFactory) -> VNode {
-        match self {
-            Some(node) => VNode::Portal(VPortal {
-                link_idx: node.link_idx.clone(),
-                scope_id: node.scope_id.clone(),
-                node: node.node,
-            }),
-            None => {
-                //
-                todo!()
-            }
-        }
-    }
-}
-
-impl IntoVNode<'_> for VPortal {
-    fn into_vnode(self, _cx: NodeFactory) -> VNode {
-        VNode::Portal(self)
+impl<'a> IntoVNode<'a> for &Option<VNode<'a>> {
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        let r = self.as_ref().map(|f| f.decouple());
+        cx.fragment_from_iter(r)
     }
 }
 
-impl IntoVNode<'_> for &VPortal {
-    fn into_vnode(self, _cx: NodeFactory) -> VNode {
-        VNode::Portal(VPortal {
-            link_idx: self.link_idx.clone(),
-            scope_id: self.scope_id.clone(),
-            node: self.node,
-        })
+impl<'a> IntoVNode<'a> for &VNode<'a> {
+    fn into_vnode(self, _cx: NodeFactory<'a>) -> VNode<'a> {
+        self.decouple()
     }
 }

+ 38 - 31
packages/core/src/scope.rs

@@ -31,7 +31,28 @@ use bumpalo::{boxed::Box as BumpBox, Bump};
 ///     cx.render(rsx!{ div {"Hello, {props.name}"} })
 /// }
 /// ```
-pub type Context<'a> = &'a Scope;
+pub struct Scope<'a, P> {
+    pub scope: &'a ScopeState,
+    pub props: &'a P,
+}
+
+impl<P> Copy for Scope<'_, P> {}
+impl<P> Clone for Scope<'_, P> {
+    fn clone(&self) -> Self {
+        Self {
+            scope: self.scope,
+            props: self.props,
+        }
+    }
+}
+
+impl<'a, P> std::ops::Deref for Scope<'a, P> {
+    // rust will auto deref again to the original 'a lifetime at the call site
+    type Target = &'a ScopeState;
+    fn deref(&self) -> &Self::Target {
+        &self.scope
+    }
+}
 
 /// A component's unique identifier.
 ///
@@ -50,8 +71,8 @@ pub struct ScopeId(pub usize);
 ///
 /// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
 /// use case they might have.
-pub struct Scope {
-    pub(crate) parent_scope: Option<*mut Scope>,
+pub struct ScopeState {
+    pub(crate) parent_scope: Option<*mut ScopeState>,
 
     pub(crate) container: ElementId,
 
@@ -67,7 +88,7 @@ pub struct Scope {
 
     pub(crate) frames: [BumpFrame; 2],
 
-    pub(crate) caller: *const dyn Fn(&Scope) -> Element,
+    pub(crate) caller: *const dyn Fn(&ScopeState) -> Element,
 
     pub(crate) items: RefCell<SelfReferentialItems<'static>>,
 
@@ -87,7 +108,7 @@ pub struct SelfReferentialItems<'a> {
 }
 
 // Public methods exposed to libraries and components
-impl Scope {
+impl ScopeState {
     /// Get the subtree ID that this scope belongs to.
     ///
     /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
@@ -331,20 +352,15 @@ impl Scope {
     ///     cx.render(lazy_tree)
     /// }
     ///```
-    pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VPortal> {
-        let bump = &self.wip_frame().bump;
-
-        let owned_node: VNode<'src> = rsx.map(|f| f.call(NodeFactory { bump }))?;
-        let alloced_vnode: &'src mut VNode<'src> = bump.alloc(owned_node);
-        let node_ptr: *mut VNode<'src> = alloced_vnode as *mut _;
-
-        let node: *mut VNode<'static> = unsafe { std::mem::transmute(node_ptr) };
-
-        Some(VPortal {
-            scope_id: Cell::new(Some(self.our_arena_idx)),
-            link_idx: Cell::new(0),
-            node,
-        })
+    pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VNode<'src>> {
+        let fac = NodeFactory {
+            bump: &self.wip_frame().bump,
+        };
+        match rsx {
+            Some(s) => Some(s.call(fac)),
+            None => todo!(),
+        }
+        // rsx.map(|f| f.call(fac))
     }
 
     /// Store a value between renders
@@ -451,7 +467,7 @@ impl Scope {
 
 pub(crate) struct BumpFrame {
     pub bump: Bump,
-    pub nodes: RefCell<Vec<*const VNode<'static>>>,
+    pub nodes: Cell<*const VNode<'static>>,
 }
 impl BumpFrame {
     pub(crate) fn new(capacity: usize) -> Self {
@@ -463,21 +479,12 @@ impl BumpFrame {
             is_static: false,
         });
         let node = bump.alloc(VNode::Text(unsafe { std::mem::transmute(node) }));
-        let nodes = RefCell::new(vec![node as *const _]);
+        let nodes = Cell::new(node as *const _);
         Self { bump, nodes }
     }
-
-    pub(crate) fn assign_nodelink(&self, node: &VPortal) {
-        let mut nodes = self.nodes.borrow_mut();
-
-        let len = nodes.len();
-        nodes.push(node.node);
-
-        node.link_idx.set(len);
-    }
 }
 
 #[test]
 fn sizeof() {
-    dbg!(std::mem::size_of::<Scope>());
+    dbg!(std::mem::size_of::<ScopeState>());
 }

+ 51 - 85
packages/core/src/scopearena.rs

@@ -24,9 +24,9 @@ pub(crate) struct ScopeArena {
     bump: Bump,
     pub pending_futures: RefCell<FxHashSet<ScopeId>>,
     scope_counter: Cell<usize>,
-    pub scopes: RefCell<FxHashMap<ScopeId, *mut Scope>>,
+    pub scopes: RefCell<FxHashMap<ScopeId, *mut ScopeState>>,
     pub heuristics: RefCell<FxHashMap<FcSlot, Heuristic>>,
-    free_scopes: RefCell<Vec<*mut Scope>>,
+    free_scopes: RefCell<Vec<*mut ScopeState>>,
     nodes: RefCell<Slab<*const VNode<'static>>>,
     pub(crate) sender: UnboundedSender<SchedulerMsg>,
 }
@@ -68,23 +68,23 @@ impl ScopeArena {
     /// Safety:
     /// - Obtaining a mutable refernece to any Scope is unsafe
     /// - Scopes use interior mutability when sharing data into components
-    pub(crate) fn get_scope(&self, id: &ScopeId) -> Option<&Scope> {
-        unsafe { self.scopes.borrow().get(id).map(|f| &**f) }
+    pub(crate) fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> {
+        unsafe { self.scopes.borrow().get(&id).map(|f| &**f) }
     }
 
-    pub(crate) unsafe fn get_scope_raw(&self, id: &ScopeId) -> Option<*mut Scope> {
-        self.scopes.borrow().get(id).copied()
+    pub(crate) unsafe fn get_scope_raw(&self, id: ScopeId) -> Option<*mut ScopeState> {
+        self.scopes.borrow().get(&id).copied()
     }
 
-    pub(crate) unsafe fn get_scope_mut(&self, id: &ScopeId) -> Option<&mut Scope> {
-        self.scopes.borrow().get(id).map(|s| &mut **s)
+    pub(crate) unsafe fn get_scope_mut(&self, id: ScopeId) -> Option<&mut ScopeState> {
+        self.scopes.borrow().get(&id).map(|s| &mut **s)
     }
 
     pub(crate) fn new_with_key(
         &self,
         fc_ptr: *const (),
-        caller: *const dyn Fn(&Scope) -> Element,
-        parent_scope: Option<*mut Scope>,
+        caller: *const dyn Fn(&ScopeState) -> Element,
+        parent_scope: Option<*mut ScopeState>,
         container: ElementId,
         height: u32,
         subtree: u32,
@@ -102,27 +102,27 @@ impl ScopeArena {
             scope.our_arena_idx = new_scope_id;
             scope.container = container;
 
-            scope.frames[0].nodes.get_mut().push({
-                let vnode = scope.frames[0]
-                    .bump
-                    .alloc(VNode::Text(scope.frames[0].bump.alloc(VText {
-                        dom_id: Default::default(),
-                        is_static: false,
-                        text: "",
-                    })));
-                unsafe { std::mem::transmute(vnode as *mut VNode) }
-            });
-
-            scope.frames[1].nodes.get_mut().push({
-                let vnode = scope.frames[1]
-                    .bump
-                    .alloc(VNode::Text(scope.frames[1].bump.alloc(VText {
-                        dom_id: Default::default(),
-                        is_static: false,
-                        text: "",
-                    })));
-                unsafe { std::mem::transmute(vnode as *mut VNode) }
-            });
+            // scope.frames[0].nodes.get_mut().push({
+            //     let vnode = scope.frames[0]
+            //         .bump
+            //         .alloc(VNode::Text(scope.frames[0].bump.alloc(VText {
+            //             dom_id: Default::default(),
+            //             is_static: false,
+            //             text: "",
+            //         })));
+            //     unsafe { std::mem::transmute(vnode as *mut VNode) }
+            // });
+
+            // scope.frames[1].nodes.get_mut().push({
+            //     let vnode = scope.frames[1]
+            //         .bump
+            //         .alloc(VNode::Text(scope.frames[1].bump.alloc(VText {
+            //             dom_id: Default::default(),
+            //             is_static: false,
+            //             text: "",
+            //         })));
+            //     unsafe { std::mem::transmute(vnode as *mut VNode) }
+            // });
 
             let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
             debug_assert!(any_item.is_none());
@@ -136,31 +136,9 @@ impl ScopeArena {
                 }
             };
 
-            let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
-
-            frames[0].nodes.get_mut().push({
-                let vnode = frames[0]
-                    .bump
-                    .alloc(VNode::Text(frames[0].bump.alloc(VText {
-                        dom_id: Default::default(),
-                        is_static: false,
-                        text: "",
-                    })));
-                unsafe { std::mem::transmute(vnode as *mut VNode) }
-            });
-
-            frames[1].nodes.get_mut().push({
-                let vnode = frames[1]
-                    .bump
-                    .alloc(VNode::Text(frames[1].bump.alloc(VText {
-                        dom_id: Default::default(),
-                        is_static: false,
-                        text: "",
-                    })));
-                unsafe { std::mem::transmute(vnode as *mut VNode) }
-            });
+            let frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
 
-            let scope = self.bump.alloc(Scope {
+            let scope = self.bump.alloc(ScopeState {
                 sender: self.sender.clone(),
                 container,
                 our_arena_idx: new_scope_id,
@@ -193,13 +171,13 @@ impl ScopeArena {
         new_scope_id
     }
 
-    pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
+    pub fn try_remove(&self, id: ScopeId) -> Option<()> {
         self.ensure_drop_safety(id);
 
         // Safety:
         // - ensure_drop_safety ensures that no references to this scope are in use
         // - this raw pointer is removed from the map
-        let scope = unsafe { &mut *self.scopes.borrow_mut().remove(id).unwrap() };
+        let scope = unsafe { &mut *self.scopes.borrow_mut().remove(&id).unwrap() };
 
         // we're just reusing scopes so we need to clear it out
         scope.hook_vals.get_mut().drain(..).for_each(|state| {
@@ -216,9 +194,6 @@ impl ScopeArena {
         scope.is_subtree_root.set(false);
         scope.subtree.set(0);
 
-        scope.frames[0].nodes.get_mut().clear();
-        scope.frames[1].nodes.get_mut().clear();
-
         scope.frames[0].bump.reset();
         scope.frames[1].bump.reset();
 
@@ -257,8 +232,6 @@ impl ScopeArena {
         self.nodes.borrow_mut().remove(id.0);
     }
 
-    // These methods would normally exist on `scope` but they need access to *all* of the scopes
-
     /// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
     /// causing UB in our tree.
     ///
@@ -271,7 +244,7 @@ impl ScopeArena {
     ///
     /// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
     /// be dropped.
-    pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
+    pub(crate) fn ensure_drop_safety(&self, scope_id: ScopeId) {
         if let Some(scope) = self.get_scope(scope_id) {
             let mut items = scope.items.borrow_mut();
 
@@ -284,7 +257,7 @@ impl ScopeArena {
                     .get()
                     .expect("VComponents should be associated with a valid Scope");
 
-                self.ensure_drop_safety(&scope_id);
+                self.ensure_drop_safety(scope_id);
 
                 let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
                 drop_props();
@@ -298,7 +271,7 @@ impl ScopeArena {
         }
     }
 
-    pub(crate) fn run_scope(&self, id: &ScopeId) -> bool {
+    pub(crate) fn run_scope(&self, id: ScopeId) -> bool {
         // Cycle to the next frame and then reset it
         // This breaks any latent references, invalidating every pointer referencing into it.
         // Remove all the outdated listeners
@@ -327,25 +300,20 @@ impl ScopeArena {
             debug_assert!(items.tasks.is_empty());
 
             // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
-            scope.wip_frame().nodes.borrow_mut().clear();
+            // scope.wip_frame().nodes
         }
 
-        let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller };
-
-        if let Some(link) = render(scope) {
-            // right now, it's a panic to render a nodelink from another scope
-            // todo: enable this. it should (reasonably) work even if it doesnt make much sense
-            assert_eq!(link.scope_id.get(), Some(*id));
-
-            // nodelinks are not assigned when called and must be done so through the create/diff phase
-            // however, we need to link this one up since it will never be used in diffing
-            scope.wip_frame().assign_nodelink(&link);
-            debug_assert_eq!(scope.wip_frame().nodes.borrow().len(), 1);
+        let render: &dyn Fn(&ScopeState) -> Element = unsafe { &*scope.caller };
 
+        if let Some(node) = render(scope) {
             if !scope.items.borrow().tasks.is_empty() {
-                self.pending_futures.borrow_mut().insert(*id);
+                self.pending_futures.borrow_mut().insert(id);
             }
 
+            let frame = scope.wip_frame();
+            let node = frame.bump.alloc(node);
+            frame.nodes.set(unsafe { std::mem::transmute(node) });
+
             // make the "wip frame" contents the "finished frame"
             // any future dipping into completed nodes after "render" will go through "fin head"
             scope.cycle_frame();
@@ -380,24 +348,22 @@ impl ScopeArena {
     }
 
     // The head of the bumpframe is the first linked NodeLink
-    pub fn wip_head(&self, id: &ScopeId) -> &VNode {
+    pub fn wip_head(&self, id: ScopeId) -> &VNode {
         let scope = self.get_scope(id).unwrap();
         let frame = scope.wip_frame();
-        let nodes = frame.nodes.borrow();
-        let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
+        let node = unsafe { &*frame.nodes.get() };
         unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
     }
 
     // The head of the bumpframe is the first linked NodeLink
-    pub fn fin_head(&self, id: &ScopeId) -> &VNode {
+    pub fn fin_head(&self, id: ScopeId) -> &VNode {
         let scope = self.get_scope(id).unwrap();
         let frame = scope.fin_frame();
-        let nodes = frame.nodes.borrow();
-        let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
+        let node = unsafe { &*frame.nodes.get() };
         unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
     }
 
-    pub fn root_node(&self, id: &ScopeId) -> &VNode {
+    pub fn root_node(&self, id: ScopeId) -> &VNode {
         self.fin_head(id)
     }
 }

+ 44 - 30
packages/core/src/virtual_dom.rs

@@ -105,7 +105,8 @@ use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc, task::Poll};
 pub struct VirtualDom {
     base_scope: ScopeId,
 
-    _root_props: Box<dyn Any>,
+    // it's stored here so the props are dropped when the VirtualDom is dropped
+    _root_caller: Box<dyn for<'r> Fn(&'r ScopeState) -> Element<'r> + 'static>,
 
     scopes: Box<ScopeArena>,
 
@@ -132,7 +133,7 @@ impl VirtualDom {
     ///
     /// # Example
     /// ```rust, ignore
-    /// fn Example(cx: Context, props: &()) -> Element  {
+    /// fn Example(cx: Scope<()>) -> Element  {
     ///     cx.render(rsx!( div { "hello world" } ))
     /// }
     ///
@@ -161,8 +162,8 @@ impl VirtualDom {
     ///     name: &'static str
     /// }
     ///
-    /// fn Example(cx: Context, props: &SomeProps) -> Element  {
-    ///     cx.render(rsx!{ div{ "hello {cx.name}" } })
+    /// fn Example(cx: Scope<SomeProps>) -> Element  {
+    ///     cx.render(rsx!{ div{ "hello {cx.props.name}" } })
     /// }
     ///
     /// let dom = VirtualDom::new(Example);
@@ -194,21 +195,36 @@ impl VirtualDom {
         sender: UnboundedSender<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
     ) -> Self {
-        let scopes = ScopeArena::new(sender.clone());
+        // move these two things onto the heap so we have stable ptrs
+        let scopes = Box::new(ScopeArena::new(sender.clone()));
+        let root_props = Box::new(root_props);
+
+        // create the root caller which will properly drop its props when the VirtualDom is dropped
+        let mut _root_caller: Box<dyn for<'r> Fn(&'r ScopeState) -> Element<'r> + 'static> =
+            Box::new(move |scope: &ScopeState| -> Element {
+                // Safety: The props at this pointer can never be moved.
+                // Also, this closure will never be ran when the VirtualDom is destroyed.
+                // This is where the root lifetime of the VirtualDom originates.
+                let props: *const P = root_props.as_ref();
+                let props = unsafe { &*props };
+                root(Scope { scope, props })
+            });
 
-        let mut caller = Box::new(move |scp: &Scope| -> Element { root(scp, &root_props) });
-        let caller_ref: *mut dyn Fn(&Scope) -> Element = caller.as_mut() as *mut _;
-        let base_scope = scopes.new_with_key(root as _, caller_ref, None, ElementId(0), 0, 0);
+        // safety: the raw pointer is aliased or used after this point.
+        let caller: *mut dyn Fn(&ScopeState) -> Element = _root_caller.as_mut();
+        let base_scope = scopes.new_with_key(root as _, caller, None, ElementId(0), 0, 0);
 
-        let pending_messages = VecDeque::new();
         let mut dirty_scopes = IndexSet::new();
         dirty_scopes.insert(base_scope);
 
+        // todo: add a pending message to the scheduler to start the scheduler?
+        let pending_messages = VecDeque::new();
+
         Self {
-            scopes: Box::new(scopes),
+            scopes,
             base_scope,
             receiver,
-            _root_props: caller,
+            _root_caller,
             pending_messages,
             dirty_scopes,
             sender,
@@ -221,17 +237,17 @@ impl VirtualDom {
     /// directly.
     ///
     /// # Example
-    pub fn base_scope(&self) -> &Scope {
-        self.get_scope(&self.base_scope).unwrap()
+    pub fn base_scope(&self) -> &ScopeState {
+        self.get_scope(self.base_scope).unwrap()
     }
 
-    /// Get the [`Scope`] for a component given its [`ScopeId`]
+    /// Get the [`ScopeState`] for a component given its [`ScopeId`]
     ///
     /// # Example
     ///
     ///
     ///
-    pub fn get_scope<'a>(&'a self, id: &ScopeId) -> Option<&'a Scope> {
+    pub fn get_scope<'a>(&'a self, id: ScopeId) -> Option<&'a ScopeState> {
         self.scopes.get_scope(id)
     }
 
@@ -388,11 +404,11 @@ impl VirtualDom {
 
             // Sort the scopes by height. Theoretically, we'll de-duplicate scopes by height
             self.dirty_scopes
-                .retain(|id| scopes.get_scope(id).is_some());
+                .retain(|id| scopes.get_scope(*id).is_some());
 
             self.dirty_scopes.sort_by(|a, b| {
-                let h1 = scopes.get_scope(a).unwrap().height;
-                let h2 = scopes.get_scope(b).unwrap().height;
+                let h1 = scopes.get_scope(*a).unwrap().height;
+                let h2 = scopes.get_scope(*b).unwrap().height;
                 h1.cmp(&h2).reverse()
             });
 
@@ -400,15 +416,13 @@ impl VirtualDom {
                 if !ran_scopes.contains(&scopeid) {
                     ran_scopes.insert(scopeid);
 
-                    if self.scopes.run_scope(&scopeid) {
-                        let (old, new) = (
-                            self.scopes.wip_head(&scopeid),
-                            self.scopes.fin_head(&scopeid),
-                        );
+                    if self.scopes.run_scope(scopeid) {
+                        let (old, new) =
+                            (self.scopes.wip_head(scopeid), self.scopes.fin_head(scopeid));
                         diff_state.stack.push(DiffInstruction::Diff { new, old });
                         diff_state.stack.scope_stack.push(scopeid);
 
-                        let scope = scopes.get_scope(&scopeid).unwrap();
+                        let scope = scopes.get_scope(scopeid).unwrap();
                         diff_state.stack.element_stack.push(scope.container);
                     }
                 }
@@ -455,10 +469,10 @@ impl VirtualDom {
         let mut diff_state = DiffState::new(&self.scopes);
 
         let scope_id = self.base_scope;
-        if self.scopes.run_scope(&scope_id) {
+        if self.scopes.run_scope(scope_id) {
             diff_state
                 .stack
-                .create_node(self.scopes.fin_head(&scope_id), MountType::Append);
+                .create_node(self.scopes.fin_head(scope_id), MountType::Append);
 
             diff_state.stack.element_stack.push(ElementId(0));
             diff_state.stack.scope_stack.push(scope_id);
@@ -497,7 +511,7 @@ impl VirtualDom {
     ///
     /// let edits = dom.diff();
     /// ```
-    pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
+    pub fn hard_diff<'a>(&'a mut self, scope_id: ScopeId) -> Option<Mutations<'a>> {
         let mut diff_machine = DiffState::new(&self.scopes);
         if self.scopes.run_scope(scope_id) {
             diff_machine.force_diff = true;
@@ -519,7 +533,7 @@ impl VirtualDom {
     /// let nodes = dom.render_nodes(rsx!("div"));
     /// ```
     pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
-        let scope = self.scopes.get_scope(&self.base_scope).unwrap();
+        let scope = self.scopes.get_scope(self.base_scope).unwrap();
         let frame = scope.wip_frame();
         let factory = NodeFactory { bump: &frame.bump };
         let node = lazy_nodes.unwrap().call(factory);
@@ -731,7 +745,7 @@ impl<'a> Future for PollTasks<'a> {
         let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
 
         // Poll every scope manually
-        for fut in self.0.pending_futures.borrow().iter() {
+        for fut in self.0.pending_futures.borrow().iter().copied() {
             let scope = self.0.get_scope(fut).expect("Scope should never be moved");
 
             let mut items = scope.items.borrow_mut();
@@ -746,7 +760,7 @@ impl<'a> Future for PollTasks<'a> {
             }
 
             if unfinished_tasks.is_empty() {
-                scopes_to_clear.push(*fut);
+                scopes_to_clear.push(fut);
             }
 
             items.tasks.extend(unfinished_tasks.drain(..));

+ 5 - 5
packages/core/tests/borrowedstate.rs

@@ -10,7 +10,7 @@ fn test_borrowed_state() {
     let _ = VirtualDom::new(Parent);
 }
 
-fn Parent(cx: Context, _props: &()) -> Element {
+fn Parent(cx: Scope<()>) -> Element {
     let value = cx.use_hook(|_| String::new(), |f| &*f);
 
     cx.render(rsx! {
@@ -28,11 +28,11 @@ struct ChildProps<'a> {
     name: &'a str,
 }
 
-fn Child(cx: Context, props: &ChildProps) -> Element {
+fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
     cx.render(rsx! {
         div {
             h1 { "it's nested" }
-            Child2 { name: props.name }
+            Child2 { name: cx.props.name }
         }
     })
 }
@@ -42,8 +42,8 @@ struct Grandchild<'a> {
     name: &'a str,
 }
 
-fn Child2(cx: Context, props: &Grandchild) -> Element {
+fn Child2<'a>(cx: Scope<'a, Grandchild<'a>>) -> Element {
     cx.render(rsx! {
-        div { "Hello {props.name}!" }
+        div { "Hello {cx.props.name}!" }
     })
 }

+ 11 - 11
packages/core/tests/create_dom.rs

@@ -21,7 +21,7 @@ fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
 
 #[test]
 fn test_original_diff() {
-    static APP: Component<()> = |cx, props| {
+    static APP: Component<()> = |cx| {
         cx.render(rsx! {
             div {
                 div {
@@ -57,7 +57,7 @@ fn test_original_diff() {
 
 #[test]
 fn create() {
-    static APP: Component<()> = |cx, props| {
+    static APP: Component<()> = |cx| {
         cx.render(rsx! {
             div {
                 div {
@@ -120,7 +120,7 @@ fn create() {
 
 #[test]
 fn create_list() {
-    static APP: Component<()> = |cx, props| {
+    static APP: Component<()> = |cx| {
         cx.render(rsx! {
             {(0..3).map(|f| rsx!{ div {
                 "hello"
@@ -169,7 +169,7 @@ fn create_list() {
 
 #[test]
 fn create_simple() {
-    static APP: Component<()> = |cx, props| {
+    static APP: Component<()> = |cx| {
         cx.render(rsx! {
             div {}
             div {}
@@ -207,7 +207,7 @@ fn create_simple() {
 }
 #[test]
 fn create_components() {
-    static App: Component<()> = |cx, props| {
+    static App: Component<()> = |cx| {
         cx.render(rsx! {
             Child { "abc1" }
             Child { "abc2" }
@@ -215,15 +215,15 @@ fn create_components() {
         })
     };
 
-    #[derive(Props, PartialEq)]
-    struct ChildProps {
-        children: Element,
+    #[derive(Props)]
+    struct ChildProps<'a> {
+        children: Element<'a>,
     }
 
-    fn Child(cx: Context, props: &ChildProps) -> Element {
+    fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
         cx.render(rsx! {
             h1 {}
-            div { {&props.children} }
+            div { {&cx.props.children} }
             p {}
         })
     }
@@ -273,7 +273,7 @@ fn create_components() {
 }
 #[test]
 fn anchors() {
-    static App: Component<()> = |cx, props| {
+    static App: Component<()> = |cx| {
         cx.render(rsx! {
             {true.then(|| rsx!{ div { "hello" } })}
             {false.then(|| rsx!{ div { "goodbye" } })}

+ 1 - 1
packages/core/tests/diffing.rs

@@ -15,7 +15,7 @@ mod test_logging;
 fn new_dom() -> VirtualDom {
     const IS_LOGGING_ENABLED: bool = false;
     test_logging::set_up_logging(IS_LOGGING_ENABLED);
-    VirtualDom::new(|cx, props| todo!())
+    VirtualDom::new(|cx| todo!())
 }
 
 use DomEdit::*;

+ 20 - 20
packages/core/tests/lifecycle.rs

@@ -22,8 +22,8 @@ fn manual_diffing() {
         value: Shared<&'static str>,
     }
 
-    static App: Component<AppProps> = |cx, props| {
-        let val = props.value.lock().unwrap();
+    static App: Component<AppProps> = |cx| {
+        let val = cx.props.value.lock().unwrap();
         cx.render(rsx! { div { "{val}" } })
     };
 
@@ -46,8 +46,8 @@ fn manual_diffing() {
 
 #[test]
 fn events_generate() {
-    static App: Component<()> = |cx, _| {
-        let mut count = use_state(cx, || 0);
+    static App: Component<()> = |cx| {
+        let mut count = use_state(&cx, || 0);
 
         let inner = match *count {
             0 => {
@@ -105,8 +105,8 @@ fn events_generate() {
 
 #[test]
 fn components_generate() {
-    static App: Component<()> = |cx, _| {
-        let mut render_phase = use_state(cx, || 0);
+    static App: Component<()> = |cx| {
+        let mut render_phase = use_state(&cx, || 0);
         render_phase += 1;
 
         cx.render(match *render_phase {
@@ -122,7 +122,7 @@ fn components_generate() {
         })
     };
 
-    static Child: Component<()> = |cx, _| {
+    static Child: Component<()> = |cx| {
         cx.render(rsx! {
             h1 {}
         })
@@ -141,7 +141,7 @@ fn components_generate() {
         ]
     );
 
-    let edits = dom.hard_diff(&ScopeId(0)).unwrap();
+    let edits = dom.hard_diff(ScopeId(0)).unwrap();
     assert_eq!(
         edits.edits,
         [
@@ -153,7 +153,7 @@ fn components_generate() {
         ]
     );
 
-    let edits = dom.hard_diff(&ScopeId(0)).unwrap();
+    let edits = dom.hard_diff(ScopeId(0)).unwrap();
     assert_eq!(
         edits.edits,
         [
@@ -165,7 +165,7 @@ fn components_generate() {
         ]
     );
 
-    let edits = dom.hard_diff(&ScopeId(0)).unwrap();
+    let edits = dom.hard_diff(ScopeId(0)).unwrap();
     assert_eq!(
         edits.edits,
         [
@@ -174,13 +174,13 @@ fn components_generate() {
         ]
     );
 
-    let edits = dom.hard_diff(&ScopeId(0)).unwrap();
+    let edits = dom.hard_diff(ScopeId(0)).unwrap();
     assert_eq!(
         edits.edits,
         [CreatePlaceholder { root: 5 }, ReplaceWith { root: 4, m: 1 },]
     );
 
-    let edits = dom.hard_diff(&ScopeId(0)).unwrap();
+    let edits = dom.hard_diff(ScopeId(0)).unwrap();
     assert_eq!(
         edits.edits,
         [
@@ -192,7 +192,7 @@ fn components_generate() {
         ]
     );
 
-    let edits = dom.hard_diff(&ScopeId(0)).unwrap();
+    let edits = dom.hard_diff(ScopeId(0)).unwrap();
     assert_eq!(
         edits.edits,
         [
@@ -208,7 +208,7 @@ fn components_generate() {
         ]
     );
 
-    let edits = dom.hard_diff(&ScopeId(0)).unwrap();
+    let edits = dom.hard_diff(ScopeId(0)).unwrap();
     assert_eq!(
         edits.edits,
         [
@@ -222,8 +222,8 @@ fn components_generate() {
 #[test]
 fn component_swap() {
     // simple_logger::init();
-    static App: Component<()> = |cx, _| {
-        let mut render_phase = use_state(cx, || 0);
+    static App: Component<()> = |cx| {
+        let mut render_phase = use_state(&cx, || 0);
         render_phase += 1;
 
         cx.render(match *render_phase {
@@ -261,7 +261,7 @@ fn component_swap() {
         })
     };
 
-    static NavBar: Component<()> = |cx, _| {
+    static NavBar: Component<()> = |cx| {
         println!("running navbar");
         cx.render(rsx! {
             h1 {
@@ -271,7 +271,7 @@ fn component_swap() {
         })
     };
 
-    static NavLink: Component<()> = |cx, _| {
+    static NavLink: Component<()> = |cx| {
         println!("running navlink");
         cx.render(rsx! {
             h1 {
@@ -280,7 +280,7 @@ fn component_swap() {
         })
     };
 
-    static Dashboard: Component<()> = |cx, _| {
+    static Dashboard: Component<()> = |cx| {
         println!("running dashboard");
         cx.render(rsx! {
             div {
@@ -289,7 +289,7 @@ fn component_swap() {
         })
     };
 
-    static Results: Component<()> = |cx, _| {
+    static Results: Component<()> = |cx| {
         println!("running results");
         cx.render(rsx! {
             div {

+ 2 - 2
packages/core/tests/sharedstate.rs

@@ -13,12 +13,12 @@ mod test_logging;
 fn shared_state_test() {
     struct MySharedState(&'static str);
 
-    static App: Component<()> = |cx, props| {
+    static App: Component<()> = |cx| {
         cx.provide_state(MySharedState("world!"));
         cx.render(rsx!(Child {}))
     };
 
-    static Child: Component<()> = |cx, props| {
+    static Child: Component<()> = |cx| {
         let shared = cx.consume_state::<MySharedState>()?;
         cx.render(rsx!("Hello, {shared.0}"))
     };

+ 6 - 6
packages/core/tests/vdom_rebuild.rs

@@ -19,7 +19,7 @@ use DomEdit::*;
 
 #[test]
 fn app_runs() {
-    static App: Component<()> = |cx, props| rsx!(cx, div{"hello"} );
+    static App: Component<()> = |cx| rsx!(cx, div{"hello"} );
 
     let mut vdom = VirtualDom::new(App);
     let edits = vdom.rebuild();
@@ -27,7 +27,7 @@ fn app_runs() {
 
 #[test]
 fn fragments_work() {
-    static App: Component<()> = |cx, props| {
+    static App: Component<()> = |cx| {
         cx.render(rsx!(
             div{"hello"}
             div{"goodbye"}
@@ -41,7 +41,7 @@ fn fragments_work() {
 
 #[test]
 fn lists_work() {
-    static App: Component<()> = |cx, props| {
+    static App: Component<()> = |cx| {
         cx.render(rsx!(
             h1 {"hello"}
             {(0..6).map(|f| rsx!(span{ "{f}" }))}
@@ -54,7 +54,7 @@ fn lists_work() {
 
 #[test]
 fn conditional_rendering() {
-    static App: Component<()> = |cx, props| {
+    static App: Component<()> = |cx| {
         cx.render(rsx!(
             h1 {"hello"}
             {true.then(|| rsx!(span{ "a" }))}
@@ -87,13 +87,13 @@ fn conditional_rendering() {
 
 #[test]
 fn child_components() {
-    static App: Component<()> = |cx, props| {
+    static App: Component<()> = |cx| {
         cx.render(rsx!(
             {true.then(|| rsx!(Child { }))}
             {false.then(|| rsx!(Child { }))}
         ))
     };
-    static Child: Component<()> = |cx, props| {
+    static Child: Component<()> = |cx| {
         cx.render(rsx!(
             h1 {"hello"}
             h1 {"goodbye"}

+ 4 - 4
packages/hooks/src/use_shared_state.rs

@@ -1,4 +1,4 @@
-use dioxus_core::{prelude::Context, ScopeId};
+use dioxus_core::{ScopeId, ScopeState};
 use std::{
     cell::{Cell, Ref, RefCell, RefMut},
     collections::HashSet,
@@ -59,7 +59,7 @@ impl<T> ProvidedStateInner<T> {
 ///
 ///
 ///
-pub fn use_shared_state<'a, T: 'static>(cx: Context<'a>) -> Option<UseSharedState<'a, T>> {
+pub fn use_shared_state<'a, T: 'static>(cx: &'a ScopeState) -> Option<UseSharedState<'a, T>> {
     cx.use_hook(
         |_| {
             let scope_id = cx.scope_id();
@@ -110,7 +110,7 @@ impl<T> Drop for SharedStateInner<T> {
 }
 
 pub struct UseSharedState<'a, T: 'static> {
-    pub(crate) cx: Context<'a>,
+    pub(crate) cx: &'a ScopeState,
     pub(crate) value: &'a Rc<RefCell<T>>,
     pub(crate) root: &'a Rc<RefCell<ProvidedStateInner<T>>>,
     pub(crate) needs_notification: &'a Cell<bool>,
@@ -175,7 +175,7 @@ where
 ///
 ///
 ///
-pub fn use_provide_state<'a, T: 'static>(cx: Context<'a>, f: impl FnOnce() -> T) {
+pub fn use_provide_state<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) {
     cx.use_hook(
         |_| {
             let state: ProvidedState<T> = RefCell::new(ProvidedStateInner {

+ 4 - 5
packages/hooks/src/usecoroutine.rs

@@ -1,4 +1,4 @@
-use dioxus_core::Context;
+use dioxus_core::ScopeState;
 use futures::Future;
 use std::{
     cell::{Cell, RefCell},
@@ -7,10 +7,9 @@ use std::{
 };
 
 pub fn use_coroutine<'a, F: Future<Output = ()> + 'static>(
-    cx: Context<'a>,
+    cx: &'a ScopeState,
     mut f: impl FnMut() -> F + 'a,
-) -> CoroutineHandle {
-    //
+) -> CoroutineHandle<'a> {
     cx.use_hook(
         move |_| State {
             running: Default::default(),
@@ -56,7 +55,7 @@ struct State {
 }
 
 pub struct CoroutineHandle<'a> {
-    cx: Context<'a>,
+    cx: &'a ScopeState,
     inner: &'a State,
 }
 impl Clone for CoroutineHandle<'_> {

+ 7 - 7
packages/hooks/src/usemodel.rs

@@ -4,7 +4,7 @@
 //!
 //! In these cases, we provide `use_model` - a convenient way of abstracting over some state and async functions.
 
-use dioxus_core::prelude::Context;
+use dioxus_core::prelude::ScopeState;
 use futures::Future;
 use std::{
     cell::{Cell, Ref, RefCell, RefMut},
@@ -13,7 +13,7 @@ use std::{
     rc::Rc,
 };
 
-pub fn use_model<T: 'static>(cx: Context, f: impl FnOnce() -> T) -> UseModel<T> {
+pub fn use_model<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) -> UseModel<'a, T> {
     cx.use_hook(
         |_| UseModelInner {
             update_scheduled: Cell::new(false),
@@ -70,16 +70,16 @@ impl<'a, T: 'static> UseModel<'a, T> {
         (self.read(), self)
     }
 
-    pub fn start(&self, f: impl FnOnce() -> ModelTask) {
+    pub fn start(&self, _f: impl FnOnce() -> ModelTask) {
         todo!()
     }
 }
 
 // keep a coroutine going
-pub fn use_model_coroutine<T, F: Future<Output = ()> + 'static>(
-    cx: Context,
-    model: UseModel<T>,
-    f: impl FnOnce(AppModels) -> F,
+pub fn use_model_coroutine<'a, T, F: Future<Output = ()> + 'static>(
+    cx: &'a ScopeState,
+    _model: UseModel<T>,
+    _f: impl FnOnce(AppModels) -> F,
 ) -> UseModelCoroutine {
     cx.use_hook(
         |_| {

+ 2 - 2
packages/hooks/src/useref.rs

@@ -3,9 +3,9 @@ use std::{
     rc::Rc,
 };
 
-use dioxus_core::Context;
+use dioxus_core::ScopeState;
 
-pub fn use_ref<T: 'static>(cx: Context, f: impl FnOnce() -> T) -> UseRef<T> {
+pub fn use_ref<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) -> UseRef<'a, T> {
     cx.use_hook(
         |_| UseRefInner {
             update_scheduled: Cell::new(false),

+ 23 - 14
packages/hooks/src/usestate.rs

@@ -1,4 +1,4 @@
-use dioxus_core::prelude::Context;
+use dioxus_core::prelude::*;
 use std::{
     cell::{Cell, Ref, RefCell, RefMut},
     fmt::{Debug, Display},
@@ -6,11 +6,21 @@ use std::{
     rc::Rc,
 };
 
+pub trait UseStateA<'a, T> {
+    fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T>;
+}
+
+impl<'a, P, T> UseStateA<'a, T> for Scope<'a, P> {
+    fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T> {
+        use_state(self.scope, initial_state_fn)
+    }
+}
+
 /// Store state between component renders!
 ///
 /// ## Dioxus equivalent of useState, designed for Rust
 ///
-/// The Dioxus version of `useState` is the "king daddy" of state management. It allows you to ergonomically store and
+/// The Dioxus version of `useState` for state management inside components. It allows you to ergonomically store and
 /// modify state between component renders. When the state is updated, the component will re-render.
 ///
 /// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
@@ -34,23 +44,22 @@ use std::{
 ///
 ///
 /// Usage:
+///
 /// ```ignore
-/// const Example: FC<()> = |cx, props|{
-///     let counter = use_state(cx, || 0);
-///     let increment = |_| counter += 1;
-///     let decrement = |_| counter += 1;
+/// const Example: Component<()> = |cx| {
+///     let counter = use_state(&cx, || 0);
 ///
-///     html! {
-///         <div>
-///             <h1>"Counter: {counter}" </h1>
-///             <button onclick={increment}> "Increment" </button>
-///             <button onclick={decrement}> "Decrement" </button>
-///         </div>  
-///     }
+///     cx.render(rsx! {
+///         div {
+///             h1 { "Counter: {counter}" }
+///             button { onclick: move |_| counter += 1, "Increment" }
+///             button { onclick: move |_| counter -= 1, "Decrement" }
+///         }
+///     ))
 /// }
 /// ```
 pub fn use_state<'a, T: 'static>(
-    cx: Context<'a>,
+    cx: &'a ScopeState,
     initial_state_fn: impl FnOnce() -> T,
 ) -> UseState<'a, T> {
     cx.use_hook(

+ 8 - 8
packages/hooks/src/usestate2.rs

@@ -1,11 +1,11 @@
-use dioxus_core::prelude::Context;
-use std::{
-    borrow::{Borrow, BorrowMut},
-    cell::{Cell, Ref, RefCell, RefMut},
-    fmt::{Debug, Display},
-    ops::Not,
-    rc::Rc,
-};
+// use dioxus_core::prelude::Context;
+// use std::{
+//     borrow::{Borrow, BorrowMut},
+//     cell::{Cell, Ref, RefCell, RefMut},
+//     fmt::{Debug, Display},
+//     ops::Not,
+//     rc::Rc,
+// };
 
 // /// Store state between component renders!
 // ///

+ 9 - 9
packages/router/src/lib.rs

@@ -66,7 +66,7 @@ impl<R: Routable> RouterService<R> {
 /// This hould only be used once per app
 ///
 /// You can manually parse the route if you want, but the derived `parse` method on `Routable` will also work just fine
-pub fn use_router<R: Routable>(cx: Context, mut parse: impl FnMut(&str) -> R + 'static) -> &R {
+pub fn use_router<R: Routable>(cx: &ScopeState, mut parse: impl FnMut(&str) -> R + 'static) -> &R {
     // for the web, attach to the history api
     cx.use_hook(
         |f| {
@@ -133,12 +133,12 @@ pub fn use_router<R: Routable>(cx: Context, mut parse: impl FnMut(&str) -> R + '
     )
 }
 
-pub fn use_router_service<R: Routable>(cx: Context) -> Option<&Rc<RouterService<R>>> {
+pub fn use_router_service<R: Routable>(cx: &ScopeState) -> Option<&Rc<RouterService<R>>> {
     cx.use_hook(|_| cx.consume_state::<RouterService<R>>(), |f| f.as_ref())
 }
 
 #[derive(Props)]
-pub struct LinkProps<R: Routable> {
+pub struct LinkProps<'a, R: Routable> {
     to: R,
 
     /// The url that gets pushed to the history stack
@@ -157,16 +157,16 @@ pub struct LinkProps<R: Routable> {
     href: fn(&R) -> String,
 
     #[builder(default)]
-    children: Element,
+    children: Element<'a>,
 }
 
-pub fn Link<R: Routable>(cx: Context, props: &LinkProps<R>) -> Element {
-    let service = use_router_service::<R>(cx)?;
+pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
+    let service = use_router_service::<R>(&cx)?;
     cx.render(rsx! {
         a {
-            href: format_args!("{}", (props.href)(&props.to)),
-            onclick: move |_| service.push_route(props.to.clone()),
-            {&props.children},
+            href: format_args!("{}", (cx.props.href)(&cx.props.to)),
+            onclick: move |_| service.push_route(cx.props.to.clone()),
+            // todo!() {&cx.props.children},
         }
     })
 }

+ 7 - 10
packages/ssr/src/lib.rs

@@ -94,7 +94,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
         "{:}",
         TextRenderer {
             cfg: SsrConfig::default(),
-            root: vdom.get_scope(&scope).unwrap().root_node(),
+            root: vdom.get_scope(scope).unwrap().root_node(),
             vdom: Some(vdom)
         }
     ))
@@ -158,9 +158,6 @@ impl<'a> TextRenderer<'a, '_> {
                 }
                 write!(f, "<!-- -->")?;
             }
-            VNode::Portal(link) => {
-                todo!();
-            }
             VNode::Element(el) => {
                 if self.cfg.indent {
                     for _ in 0..il {
@@ -247,7 +244,7 @@ impl<'a> TextRenderer<'a, '_> {
                 let idx = vcomp.associated_scope.get().unwrap();
 
                 if let (Some(vdom), false) = (self.vdom, self.cfg.skip_components) {
-                    let new_node = vdom.get_scope(&idx).unwrap().root_node();
+                    let new_node = vdom.get_scope(idx).unwrap().root_node();
                     self.html_render(new_node, f, il + 1)?;
                 } else {
                 }
@@ -301,13 +298,13 @@ mod tests {
     use dioxus_core_macro::*;
     use dioxus_html as dioxus_elements;
 
-    static SIMPLE_APP: Component<()> = |cx, _| {
+    static SIMPLE_APP: Component<()> = |cx| {
         cx.render(rsx!(div {
             "hello world!"
         }))
     };
 
-    static SLIGHTLY_MORE_COMPLEX: Component<()> = |cx, _| {
+    static SLIGHTLY_MORE_COMPLEX: Component<()> = |cx| {
         cx.render(rsx! {
             div {
                 title: "About W3Schools"
@@ -326,14 +323,14 @@ mod tests {
         })
     };
 
-    static NESTED_APP: Component<()> = |cx, _| {
+    static NESTED_APP: Component<()> = |cx| {
         cx.render(rsx!(
             div {
                 SIMPLE_APP {}
             }
         ))
     };
-    static FRAGMENT_APP: Component<()> = |cx, _| {
+    static FRAGMENT_APP: Component<()> = |cx| {
         cx.render(rsx!(
             div { "f1" }
             div { "f2" }
@@ -389,7 +386,7 @@ mod tests {
 
     #[test]
     fn styles() {
-        static STLYE_APP: Component<()> = |cx, _| {
+        static STLYE_APP: Component<()> = |cx| {
             cx.render(rsx! {
                 div { color: "blue", font_size: "46px"  }
             })

+ 1 - 1
packages/web/examples/js_bench.rs

@@ -175,7 +175,7 @@ struct ActionButtonProps<'a> {
     onclick: &'a dyn Fn(),
 }
 
-fn ActionButton(cx: Context, props: &ActionButtonProps) -> Element {
+fn ActionButton(cx: Scope, props: &ActionButtonProps) -> Element {
     rsx!(cx, div { class: "col-sm-6 smallpad"
         button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}",  onclick: move |_| (props.onclick)(),
             "{props.name}"