diff --git a/examples/borrowed.rs b/examples/borrowed.rs index bf779f3b5..35ec2cfba 100644 --- a/examples/borrowed.rs +++ b/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 = 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}"} }) diff --git a/examples/calculator.rs b/examples/calculator.rs index d17d35b84..442ddb7f8 100644 --- a/examples/calculator.rs +++ b/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} diff --git a/examples/coroutine.rs b/examples/coroutine.rs index 2fa8e25e6..61bed91c3 100644 --- a/examples/coroutine.rs +++ b/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" } diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index 0330357a5..44fe202e1 100644 --- a/examples/framework_benchmark.rs +++ b/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}" diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 7eaf74d7d..e6c874ba5 100644 --- a/examples/hello_world.rs +++ b/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!" } )) diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index 518f60101..d0a9e7290 100644 --- a/examples/pattern_model.rs +++ b/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}" diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 7a69c6de9..e27d4c828 100644 --- a/examples/rsx_usage.rs +++ b/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) -> Element { + pub fn Baller(_: ScopeState) -> 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!() } diff --git a/examples/ssr.rs b/examples/ssr.rs index 87bb4328e..e23251ada 100644 --- a/examples/ssr.rs +++ b/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 } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 9d8969c33..395b86696 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -85,7 +85,7 @@ pub struct TodoEntryProps { todo: Rc, } -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; diff --git a/examples/web_tick.rs b/examples/web_tick.rs index a1913e766..3a24ce523 100644 --- a/examples/web_tick.rs +++ b/examples/web_tick.rs @@ -45,7 +45,7 @@ struct RowProps { row_id: usize, label: Label, } -fn Row((cx, props): Scope) -> Element { +fn Row((cx, props): ScopeState) -> Element { let [adj, col, noun] = props.label.0; cx.render(rsx! { tr { diff --git a/packages/core/benches/jsframework.rs b/packages/core/benches/jsframework.rs index f8e53bc88..4f5f762d1 100644 --- a/packages/core/benches/jsframework.rs +++ b/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 { diff --git a/packages/core/examples/borrowed.rs b/packages/core/examples/borrowed.rs new file mode 100644 index 000000000..71a4f2fb9 --- /dev/null +++ b/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); +// } diff --git a/packages/core/examples/handler_thru_props.rs b/packages/core/examples/handler_thru_props.rs index 368a58894..cb22bb9d7 100644 --- a/packages/core/examples/handler_thru_props.rs +++ b/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 { diff --git a/packages/core/examples/hooks.rs b/packages/core/examples/hooks.rs new file mode 100644 index 000000000..d4d217fde --- /dev/null +++ b/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" } + )) +} diff --git a/packages/core/examples/rows.rs b/packages/core/examples/rows.rs index 40ab0af7a..2fdce90b8 100644 --- a/packages/core/examples/rows.rs +++ b/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) -> 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}" } } diff --git a/packages/core/examples/works.rs b/packages/core/examples/works.rs index 7e49da1ce..f96c40e88 100644 --- a/packages/core/examples/works.rs +++ b/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}!" } }) } diff --git a/packages/core/src/component.rs b/packages/core/src/component.rs index 62ad62f70..f8d3c9727 100644 --- a/packages/core/src/component.rs +++ b/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(Element); -impl FragmentBuilder { - pub fn children(self, children: Option) -> FragmentBuilder { +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 FragmentBuilder { - 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 FragmentBuilder { /// }) /// } /// ``` -impl Properties for FragmentProps { - type Builder = FragmentBuilder; +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() } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index a738faeeb..35f7470ba 100644 --- a/packages/core/src/diff.rs +++ b/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) } diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index ce9ad0b2b..14d7bd123 100644 --- a/packages/core/src/lib.rs +++ b/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; - pub type Component

= for<'a> fn(Context<'a>, &'a P) -> Element; + pub type Element<'a> = Option>; + pub type Component

= 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; } diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index aedd5b90c..9514f83f9 100644 --- a/packages/core/src/nodes.rs +++ b/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>>, } @@ -355,6 +332,7 @@ impl EventHandler<'_> { self.callback.replace(None); } } + type ListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(Arc) + '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, - pub(crate) scope_id: Cell>, - 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

( &self, - component: fn(Context<'a>, &'a P) -> Element, + component: fn(Scope<'a, P>) -> Element, props: P, key: Option, ) -> 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>, - ) -> 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 { - fn into_vnode(self, _cx: NodeFactory) -> VNode { - match self { - Some(node) => VNode::Portal(node), - None => { - todo!() - } - } +impl<'a> IntoVNode<'a> for &Option> { + fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> { + let r = self.as_ref().map(|f| f.decouple()); + cx.fragment_from_iter(r) } } -// essentially passing elements through props -// just build a new element in place -impl IntoVNode<'_> for &Option { - 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 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() } } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 9f30019d1..5d07b3920 100644 --- a/packages/core/src/scope.rs +++ b/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

Copy for Scope<'_, P> {} +impl

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>, @@ -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>) -> Option { - 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>) -> Option> { + 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>>, + 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::()); + dbg!(std::mem::size_of::()); } diff --git a/packages/core/src/scopearena.rs b/packages/core/src/scopearena.rs index 4f871e401..9716a3ae1 100644 --- a/packages/core/src/scopearena.rs +++ b/packages/core/src/scopearena.rs @@ -24,9 +24,9 @@ pub(crate) struct ScopeArena { bump: Bump, pub pending_futures: RefCell>, scope_counter: Cell, - pub scopes: RefCell>, + pub scopes: RefCell>, pub heuristics: RefCell>, - free_scopes: RefCell>, + free_scopes: RefCell>, nodes: RefCell>>, pub(crate) sender: UnboundedSender, } @@ -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[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[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)]; + let 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 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) } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 4980f15c5..63d64efc7 100644 --- a/packages/core/src/virtual_dom.rs +++ b/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, + // it's stored here so the props are dropped when the VirtualDom is dropped + _root_caller: Box Fn(&'r ScopeState) -> Element<'r> + 'static>, scopes: Box, @@ -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) -> Element { + /// cx.render(rsx!{ div{ "hello {cx.props.name}" } }) /// } /// /// let dom = VirtualDom::new(Example); @@ -194,21 +195,36 @@ impl VirtualDom { sender: UnboundedSender, receiver: UnboundedReceiver, ) -> 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); - 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); + // create the root caller which will properly drop its props when the VirtualDom is dropped + let mut _root_caller: Box 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 }) + }); + + // 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> { + pub fn hard_diff<'a>(&'a mut self, scope_id: ScopeId) -> Option> { 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>) -> &'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(..)); diff --git a/packages/core/tests/borrowedstate.rs b/packages/core/tests/borrowedstate.rs index fbfcf5096..f58e97635 100644 --- a/packages/core/tests/borrowedstate.rs +++ b/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}!" } }) } diff --git a/packages/core/tests/create_dom.rs b/packages/core/tests/create_dom.rs index f6e1d57e6..a37fd85c3 100644 --- a/packages/core/tests/create_dom.rs +++ b/packages/core/tests/create_dom.rs @@ -21,7 +21,7 @@ fn new_dom(app: Component

, 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" } })} diff --git a/packages/core/tests/diffing.rs b/packages/core/tests/diffing.rs index 5811da41d..a1afb8aa2 100644 --- a/packages/core/tests/diffing.rs +++ b/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::*; diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index 1a7c4075e..d3976fdea 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -22,8 +22,8 @@ fn manual_diffing() { value: Shared<&'static str>, } - static App: Component = |cx, props| { - let val = props.value.lock().unwrap(); + static App: Component = |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 { diff --git a/packages/core/tests/sharedstate.rs b/packages/core/tests/sharedstate.rs index 4e8b84484..846b9d7f1 100644 --- a/packages/core/tests/sharedstate.rs +++ b/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::()?; cx.render(rsx!("Hello, {shared.0}")) }; diff --git a/packages/core/tests/vdom_rebuild.rs b/packages/core/tests/vdom_rebuild.rs index fcbcfbe43..b4da2418e 100644 --- a/packages/core/tests/vdom_rebuild.rs +++ b/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"} diff --git a/packages/hooks/src/use_shared_state.rs b/packages/hooks/src/use_shared_state.rs index 1a52217de..c051bdb92 100644 --- a/packages/hooks/src/use_shared_state.rs +++ b/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 ProvidedStateInner { /// /// /// -pub fn use_shared_state<'a, T: 'static>(cx: Context<'a>) -> Option> { +pub fn use_shared_state<'a, T: 'static>(cx: &'a ScopeState) -> Option> { cx.use_hook( |_| { let scope_id = cx.scope_id(); @@ -110,7 +110,7 @@ impl Drop for SharedStateInner { } pub struct UseSharedState<'a, T: 'static> { - pub(crate) cx: Context<'a>, + pub(crate) cx: &'a ScopeState, pub(crate) value: &'a Rc>, pub(crate) root: &'a Rc>>, pub(crate) needs_notification: &'a Cell, @@ -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 = RefCell::new(ProvidedStateInner { diff --git a/packages/hooks/src/usecoroutine.rs b/packages/hooks/src/usecoroutine.rs index 380d06284..f04fb4f12 100644 --- a/packages/hooks/src/usecoroutine.rs +++ b/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 + '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<'_> { diff --git a/packages/hooks/src/usemodel.rs b/packages/hooks/src/usemodel.rs index de991df68..47981af0f 100644 --- a/packages/hooks/src/usemodel.rs +++ b/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(cx: Context, f: impl FnOnce() -> T) -> UseModel { +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 + 'static>( - cx: Context, - model: UseModel, - f: impl FnOnce(AppModels) -> F, +pub fn use_model_coroutine<'a, T, F: Future + 'static>( + cx: &'a ScopeState, + _model: UseModel, + _f: impl FnOnce(AppModels) -> F, ) -> UseModelCoroutine { cx.use_hook( |_| { diff --git a/packages/hooks/src/useref.rs b/packages/hooks/src/useref.rs index c1362f4da..f80572293 100644 --- a/packages/hooks/src/useref.rs +++ b/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(cx: Context, f: impl FnOnce() -> T) -> UseRef { +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), diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index c1156f4a3..0c5bb708a 100644 --- a/packages/hooks/src/usestate.rs +++ b/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; /// -/// html! { -///

-///

"Counter: {counter}"

-/// -/// -///
-/// } +/// ```ignore +/// const Example: Component<()> = |cx| { +/// let counter = use_state(&cx, || 0); +/// +/// 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( diff --git a/packages/hooks/src/usestate2.rs b/packages/hooks/src/usestate2.rs index df778f0f1..6cffc8e95 100644 --- a/packages/hooks/src/usestate2.rs +++ b/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! // /// diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index 54411239e..cadfb8218 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -66,7 +66,7 @@ impl RouterService { /// 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(cx: Context, mut parse: impl FnMut(&str) -> R + 'static) -> &R { +pub fn use_router(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(cx: Context, mut parse: impl FnMut(&str) -> R + ' ) } -pub fn use_router_service(cx: Context) -> Option<&Rc>> { +pub fn use_router_service(cx: &ScopeState) -> Option<&Rc>> { cx.use_hook(|_| cx.consume_state::>(), |f| f.as_ref()) } #[derive(Props)] -pub struct LinkProps { +pub struct LinkProps<'a, R: Routable> { to: R, /// The url that gets pushed to the history stack @@ -157,16 +157,16 @@ pub struct LinkProps { href: fn(&R) -> String, #[builder(default)] - children: Element, + children: Element<'a>, } -pub fn Link(cx: Context, props: &LinkProps) -> Element { - let service = use_router_service::(cx)?; +pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element { + let service = use_router_service::(&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}, } }) } diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index e092daca7..80de92680 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -94,7 +94,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option { "{:}", 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" } }) diff --git a/packages/web/examples/js_bench.rs b/packages/web/examples/js_bench.rs index eeb7dc622..e81b1f04b 100644 --- a/packages/web/examples/js_bench.rs +++ b/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}"