diff --git a/packages/core/src/fragment.rs b/packages/core/src/fragment.rs index 9fc1d3c62..05dec6fda 100644 --- a/packages/core/src/fragment.rs +++ b/packages/core/src/fragment.rs @@ -76,7 +76,7 @@ impl FragmentBuilder { /// children: Element /// } /// -/// fn CustomCard(cx: Scope) -> Element { +/// fn CustomCard(cx: CardProps) -> Element { /// cx.render(rsx!{ /// div { /// h1 {"Title card"} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index daf94e0ec..cab8e3b33 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -50,7 +50,7 @@ pub(crate) mod innerlude { /// ## Rust-Style /// /// ```rust, ignore - /// fn example(cx: Scope) -> Element { + /// fn example(cx: Props) -> Element { /// // ... /// } /// @@ -60,7 +60,7 @@ pub(crate) mod innerlude { /// ``` /// ## React-Style /// ```rust, ignore - /// fn Example(cx: Scope) -> Element { + /// fn Example(cx: Props) -> Element { /// // ... /// } /// diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 8a0fb4823..b502cfc23 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -461,7 +461,7 @@ impl VComponent { /// async fn(Scope<'_>) -> Element; /// /// // With explicit props - /// fn(Scope) -> Element; + /// fn(Props) -> Element; /// async fn(Scope>) -> Element; /// ``` pub fn new + 'static, P: 'static>( @@ -649,7 +649,7 @@ impl AttributeValue { } /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`] - pub fn any_value(&self, value: T) -> AttributeValue { + pub fn any_value(value: T) -> AttributeValue { AttributeValue::Any(Box::new(value)) } } diff --git a/packages/core/src/runtime.rs b/packages/core/src/runtime.rs index 6450675af..6af1a6329 100644 --- a/packages/core/src/runtime.rs +++ b/packages/core/src/runtime.rs @@ -131,7 +131,7 @@ impl Runtime { /// } /// } /// -/// fn Component(cx: Scope) -> Element { +/// fn Component(cx: ComponentProps) -> Element { /// cx.use_hook(|| RuntimeGuard::new(cx.runtime.clone())); /// /// render! { div {} } diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 4e15cb31c..f01e43f11 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -383,4 +383,9 @@ impl ScopeId { pub fn schedule_update(&self) -> Arc { with_scope(*self, |cx| cx.schedule_update()).expect("to be in a dioxus runtime") } + + /// Get the height of the current scope + pub fn height(self) -> u32 { + with_scope(self, |cx| cx.height()).expect("to be in a dioxus runtime") + } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index e6fbf4252..90415416e 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -34,7 +34,7 @@ use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc}; /// title: String /// } /// -/// fn App(cx: Scope) -> Element { +/// fn App(cx: AppProps) -> Element { /// cx.render(rsx!( /// div {"hello, {cx.title}"} /// )) @@ -55,7 +55,7 @@ use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc}; /// static ROUTES: &str = ""; /// /// #[component] -/// fn App(cx: Scope) -> Element { +/// fn App(cx: AppProps) -> Element { /// cx.render(rsx!( /// NavBar { routes: ROUTES } /// Title { "{cx.title}" } @@ -247,7 +247,7 @@ impl VirtualDom { /// name: &'static str /// } /// - /// fn Example(cx: Scope) -> Element { + /// fn Example(cx: SomeProps) -> Element { /// cx.render(rsx!{ div{ "hello {cx.name}" } }) /// } /// diff --git a/packages/core/tests/attr_cleanup.rs b/packages/core/tests/attr_cleanup.rs index f4ca3641f..d545f414e 100644 --- a/packages/core/tests/attr_cleanup.rs +++ b/packages/core/tests/attr_cleanup.rs @@ -2,10 +2,8 @@ //! //! This tests to ensure we clean it up -use bumpalo::Bump; use dioxus::core::{ElementId, Mutation::*}; use dioxus::prelude::*; -use dioxus_core::BorrowedAttributeValue; #[test] fn attrs_cycle() { @@ -20,8 +18,6 @@ fn attrs_cycle() { } }); - let bump = Bump::new(); - assert_eq!( dom.rebuild_to_vec().santize().edits, [ @@ -59,13 +55,13 @@ fn attrs_cycle() { AssignId { path: &[0], id: ElementId(3) }, SetAttribute { name: "class", - value: BorrowedAttributeValue::Text("3"), + value: dioxus_core::AttributeValue::Text("3".to_string()), id: ElementId(3), ns: None }, SetAttribute { name: "id", - value: BorrowedAttributeValue::Text("3"), + value: dioxus_core::AttributeValue::Text("3".to_string()), id: ElementId(3), ns: None }, diff --git a/packages/core/tests/boolattrs.rs b/packages/core/tests/boolattrs.rs index e98eb745b..f4ff36420 100644 --- a/packages/core/tests/boolattrs.rs +++ b/packages/core/tests/boolattrs.rs @@ -1,11 +1,9 @@ -use bumpalo::Bump; use dioxus::core::{ElementId, Mutation::*}; use dioxus::prelude::*; #[test] fn bool_test() { - let mut app = VirtualDom::new(|cx| render!(div { hidden: false }))); - let bump = Bump::new(); + let mut app = VirtualDom::new(|| render!(div { hidden: false })); assert_eq!( app.rebuild_to_vec().santize().edits, diff --git a/packages/core/tests/context_api.rs b/packages/core/tests/context_api.rs index 51b2cb148..f0e5a82b4 100644 --- a/packages/core/tests/context_api.rs +++ b/packages/core/tests/context_api.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; #[test] fn state_shares() { fn app() -> Element { - cx.provide_context(generation() as i32); + provide_context(generation() as i32); render!(child_1 {}) } @@ -14,7 +14,7 @@ fn state_shares() { } fn child_2() -> Element { - let value = cx.consume_context::().unwrap(); + let value = consume_context::().unwrap(); render!("Value is {value}") } diff --git a/packages/core/tests/create_dom.rs b/packages/core/tests/create_dom.rs index 0ca944c1a..6070d0488 100644 --- a/packages/core/tests/create_dom.rs +++ b/packages/core/tests/create_dom.rs @@ -75,7 +75,7 @@ fn create() { #[test] fn create_list() { - let mut dom = VirtualDom::new(|cx| render! {(0..3).map(|f| render!( div { "hello" } ))})); + let mut dom = VirtualDom::new(|| render! {(0..3).map(|f| render!( div { "hello" } ))}); let _edits = dom.rebuild_to_vec().santize(); @@ -129,12 +129,12 @@ fn create_components() { } }); - #[derive(Props)] - struct ChildProps<'a> { + #[derive(Props, Clone, PartialEq)] + struct ChildProps { children: Element, } - fn Child<'a>(cx: ChildProps) -> Element { + fn Child(cx: ChildProps) -> Element { render! { h1 {} div { &cx.children } diff --git a/packages/core/tests/create_passthru.rs b/packages/core/tests/create_passthru.rs index 65b60c2a6..c7f0090b4 100644 --- a/packages/core/tests/create_passthru.rs +++ b/packages/core/tests/create_passthru.rs @@ -16,7 +16,7 @@ fn nested_passthru_creates() { } #[component] - fn PassThru<'a>(children: Element) -> Element { + fn PassThru(children: Element) -> Element { render!(children) } diff --git a/packages/core/tests/cycle.rs b/packages/core/tests/cycle.rs index ab89c2dc8..803f71098 100644 --- a/packages/core/tests/cycle.rs +++ b/packages/core/tests/cycle.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; /// As we clean up old templates, the ID for the node should cycle #[test] fn cycling_elements() { - let mut dom = VirtualDom::new(|cx| match generation() % 2 { + let mut dom = VirtualDom::new(|| match generation() % 2 { 0 => render! { div { "wasd" } }, 1 => render! { div { "abcd" } }, _ => unreachable!(), diff --git a/packages/core/tests/diff_component.rs b/packages/core/tests/diff_component.rs index ee1082adf..d78dc4487 100644 --- a/packages/core/tests/diff_component.rs +++ b/packages/core/tests/diff_component.rs @@ -8,11 +8,11 @@ use dioxus::prelude::*; #[test] fn component_swap() { fn app() -> Element { - let render_phase = cx.use_hook(|| 0); + let mut render_phase = use_signal(|| 0); - *render_phase += 1; + render_phase += 1; - match *render_phase { + match *render_phase() { 0 => render! { nav_bar {} dash_board {} diff --git a/packages/core/tests/error_boundary.rs b/packages/core/tests/error_boundary.rs index 50e226b19..111628ef8 100644 --- a/packages/core/tests/error_boundary.rs +++ b/packages/core/tests/error_boundary.rs @@ -19,7 +19,7 @@ fn app() -> Element { } } -fn NoneChild(_) -> Element { +fn NoneChild() -> Element { None } diff --git a/packages/core/tests/fuzzing.rs b/packages/core/tests/fuzzing.rs index 5e5d1ca9b..4f39be4d6 100644 --- a/packages/core/tests/fuzzing.rs +++ b/packages/core/tests/fuzzing.rs @@ -1,8 +1,8 @@ #![cfg(not(miri))] -use dioxus::prelude::Props; -use dioxus_core::*; -use std::{cfg, collections::HashSet}; +use dioxus::prelude::*; +use dioxus_core::{prelude::EventHandler, AttributeValue, DynamicNode, VComponent, *}; +use std::{cfg, collections::HashSet, default::Default}; fn random_ns() -> Option<&'static str> { let namespace = rand::random::() % 2; @@ -38,7 +38,7 @@ fn create_random_template_node( template_idx: &mut usize, attr_idx: &mut usize, depth: usize, -) -> TemplateNode<'static> { +) -> TemplateNode { match rand::random::() % 4 { 0 => { let attrs = { @@ -94,7 +94,7 @@ fn create_random_template_node( } fn generate_paths( - node: &TemplateNode<'static>, + node: &TemplateNode, current_path: &[u8], node_paths: &mut Vec>, attr_paths: &mut Vec>, @@ -166,58 +166,48 @@ fn create_random_template(name: &'static str) -> (Template, Vec ) } -fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode { - let range = if depth > 5 { 1 } else { 4 }; +fn create_random_dynamic_node(depth: usize) -> DynamicNode { + let range = if depth > 5 { 1 } else { 3 }; match rand::random::() % range { 0 => DynamicNode::Placeholder(Default::default()), - 1 => cx.make_node((0..(rand::random::() % 5)).map(|_| { - VNode::new( - None, - Template { - name: concat!(file!(), ":", line!(), ":", column!(), ":0"), - roots: &[TemplateNode::Dynamic { id: 0 }], - node_paths: &[&[0]], - attr_paths: &[], - }, - bumpalo::collections::Vec::new_in(cx.bump()), - cx.bump().alloc([cx.component( - create_random_element, - DepthProps { depth, root: false }, - "create_random_element", - )]), - &[], - ) - })), - 2 => cx.component( + 1 => (0..(rand::random::() % 5)) + .map(|_| { + VNode::new( + None, + Template { + name: concat!(file!(), ":", line!(), ":", column!(), ":0"), + roots: &[TemplateNode::Dynamic { id: 0 }], + node_paths: &[&[0]], + attr_paths: &[], + }, + Box::new([DynamicNode::Component(VComponent::new( + create_random_element, + DepthProps { depth, root: false }, + "create_random_element", + ))]), + Box::new([]), + ) + }) + .into_dyn_node(), + 2 => DynamicNode::Component(VComponent::new( create_random_element, DepthProps { depth, root: false }, "create_random_element", - ), - 3 => { - let data = String::from("borrowed data"); - let bumpped = cx.bump().alloc(data); - cx.component( - create_random_element_borrowed, - BorrowedDepthProps { borrow: &*bumpped, inner: DepthProps { depth, root: false } }, - "create_random_element_borrowed", - ) - } + )), _ => unreachable!(), } } -fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute { +fn create_random_dynamic_attr() -> Attribute { let value = match rand::random::() % 7 { - 0 => AttributeValue::Text(Box::leak( - format!("{}", rand::random::()).into_boxed_str(), - )), + 0 => AttributeValue::Text(format!("{}", rand::random::())), 1 => AttributeValue::Float(rand::random()), 2 => AttributeValue::Int(rand::random()), 3 => AttributeValue::Bool(rand::random()), - 4 => cx.any_value(rand::random::()), + 4 => AttributeValue::any_value(rand::random::()), 5 => AttributeValue::None, 6 => { - let value = cx.listener(|e: Event| println!("{:?}", e)); + let value = AttributeValue::listener(|e: Event| println!("{:?}", e)); return Attribute::new("ondata", value, None, false); } _ => unreachable!(), @@ -232,28 +222,15 @@ fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute { static mut TEMPLATE_COUNT: usize = 0; -#[derive(Props)] -struct BorrowedDepthProps<'a> { - borrow: &'a str, - inner: DepthProps, -} - -fn create_random_element_borrowed<'a>(cx: BorrowedDepthProps) -> Element { - println!("{}", cx.borrow); - let bump = cx.bump(); - let allocated = bump.alloc(Scoped { scope: cx, props: &cx.inner }); - create_random_element(allocated) -} - -#[derive(PartialEq, Props)] +#[derive(PartialEq, Props, Clone)] struct DepthProps { depth: usize, root: bool, } -fn create_random_element(cx: Scope) -> Element { +fn create_random_element(cx: DepthProps) -> Element { if rand::random::() % 10 == 0 { - cx.needs_update(); + needs_update(); } let range = if cx.root { 2 } else { 3 }; let node = match rand::random::() % range { @@ -275,24 +252,18 @@ fn create_random_element(cx: Scope) -> Element { let node = VNode::new( None, template, - bumpalo::collections::Vec::new_in(cx.bump()), - { - let dynamic_nodes: Vec<_> = dynamic_node_types - .iter() - .map(|ty| match ty { - DynamicNodeType::Text => DynamicNode::Text(VText::new(Box::leak( - format!("{}", rand::random::()).into_boxed_str(), - ))), - DynamicNodeType::Other => create_random_dynamic_node(cx, cx.depth + 1), - }) - .collect(); - cx.bump().alloc(dynamic_nodes) - }, - cx.bump().alloc( - (0..template.attr_paths.len()) - .map(|_| create_random_dynamic_attr(cx)) - .collect::>(), - ), + dynamic_node_types + .iter() + .map(|ty| match ty { + DynamicNodeType::Text => { + DynamicNode::Text(VText::new(format!("{}", rand::random::()))) + } + DynamicNodeType::Other => create_random_dynamic_node(cx.depth + 1), + }) + .collect(), + (0..template.attr_paths.len()) + .map(|_| create_random_dynamic_attr()) + .collect(), ); Some(node) } diff --git a/packages/core/tests/kitchen_sink.rs b/packages/core/tests/kitchen_sink.rs index 523d4611e..79b567c61 100644 --- a/packages/core/tests/kitchen_sink.rs +++ b/packages/core/tests/kitchen_sink.rs @@ -1,4 +1,3 @@ -use bumpalo::Bump; use dioxus::core::{ElementId, Mutation}; use dioxus::prelude::*; @@ -32,7 +31,6 @@ fn basic_syntax_is_a_template() -> Element { #[test] fn dual_stream() { let mut dom = VirtualDom::new(basic_syntax_is_a_template); - let bump = Bump::new(); let edits = dom.rebuild_to_vec().santize(); use Mutation::*; diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index 32846cc60..9da47f1ef 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -12,6 +12,7 @@ type Shared = Arc>; #[test] fn manual_diffing() { + #[derive(Clone)] struct AppProps { value: Shared<&'static str>, } @@ -42,11 +43,11 @@ fn manual_diffing() { fn events_generate() { set_event_converter(Box::new(SerializedHtmlEventConverter)); fn app() -> Element { - let count = cx.use_hook(|| 0); + let mut count = use_signal(|| 0); - match *count { + match *count() { 0 => render! { - div { onclick: move |_| *count += 1, + div { onclick: move |_| count += 1, div { "nested" } "Click me!" } @@ -80,7 +81,7 @@ fn events_generate() { // #[test] // fn components_generate() { // fn app() -> Element { -// let render_phase = cx.use_hook(|| 0); +// let render_phase = once(|| 0); // *render_phase += 1; // match *render_phase { diff --git a/packages/core/tests/miri_simple.rs b/packages/core/tests/miri_simple.rs index eb3df8320..c3d602bad 100644 --- a/packages/core/tests/miri_simple.rs +++ b/packages/core/tests/miri_simple.rs @@ -16,10 +16,10 @@ fn app_drops() { #[test] fn hooks_drop() { fn App() -> Element { - cx.use_hook(|| String::from("asd")); - cx.use_hook(|| String::from("asd")); - cx.use_hook(|| String::from("asd")); - cx.use_hook(|| String::from("asd")); + once(|| String::from("asd")); + once(|| String::from("asd")); + once(|| String::from("asd")); + once(|| String::from("asd")); render! { div {} } } @@ -34,7 +34,7 @@ fn hooks_drop() { #[test] fn contexts_drop() { fn App() -> Element { - cx.provide_context(String::from("asd")); + provide_context(String::from("asd")); render! { div { ChildComp {} } @@ -42,7 +42,7 @@ fn contexts_drop() { } fn ChildComp() -> Element { - let el = cx.consume_context::().unwrap(); + let el = consume_context::().unwrap(); render! { div { "hello {el}" } } } @@ -57,7 +57,7 @@ fn contexts_drop() { #[test] fn tasks_drop() { fn App() -> Element { - cx.spawn(async { + spawn(async { // tokio::time::sleep(std::time::Duration::from_millis(100000)).await; }); diff --git a/packages/core/tests/miri_stress.rs b/packages/core/tests/miri_stress.rs index 11677d687..24d13b23f 100644 --- a/packages/core/tests/miri_stress.rs +++ b/packages/core/tests/miri_stress.rs @@ -12,13 +12,13 @@ fn test_memory_leak() { fn app() -> Element { let val = generation(); - cx.spawn(async {}); + spawn(async {}); if val == 2 || val == 4 { return render!(()); } - let name = cx.use_hook(|| String::from("numbers: ")); + let mut name = once(|| String::from("numbers: ")); name.push_str("123 "); @@ -30,11 +30,11 @@ fn test_memory_leak() { Child {} Child {} Child {} - BorrowedChild { name: name } - BorrowedChild { name: name } - BorrowedChild { name: name } - BorrowedChild { name: name } - BorrowedChild { name: name } + BorrowedChild { name: name.clone() } + BorrowedChild { name: name.clone() } + BorrowedChild { name: name.clone() } + BorrowedChild { name: name.clone() } + BorrowedChild { name: name.clone() } ) } @@ -76,7 +76,7 @@ fn memo_works_properly() { return render!(()); } - let name = cx.use_hook(|| String::from("asd")); + let name = once(|| String::from("asd")); render!( div { "Hello, world! {name}" } @@ -89,7 +89,7 @@ fn memo_works_properly() { na: String, } - fn Child(cx: Scope) -> Element { + fn Child(cx: ChildProps) -> Element { render!( div { "goodbye world" } ) } @@ -116,8 +116,8 @@ fn free_works_on_root_hooks() { inner: Rc, } - fn app(cx: Scope) -> Element { - let name: &AppProps = cx.use_hook(|| cx.clone()); + fn app(cx: AppProps) -> Element { + let name: AppProps = once(|| cx.clone()); render!(child_component { inner: name.inner.clone() }) } @@ -146,20 +146,24 @@ fn supports_async() { let colors = use_signal(|| vec!["green", "blue", "red"]); let padding = use_signal(|| 10); - use_effect(cx, colors, |colors| async move { - sleep(Duration::from_millis(1000)).await; - colors.with_mut(|colors| colors.reverse()); + once(|| { + spawn(async move { + sleep(Duration::from_millis(1000)).await; + colors.with_mut(|colors| colors.reverse()); + }) }); - use_effect(cx, padding, |padding| async move { - sleep(Duration::from_millis(10)).await; - padding.with_mut(|padding| { - if *padding < 65 { - *padding += 1; - } else { - *padding = 5; - } - }); + once(|| { + spawn(async move { + sleep(Duration::from_millis(10)).await; + padding.with_mut(|padding| { + if *padding < 65 { + *padding += 1; + } else { + *padding = 5; + } + }); + }) }); let colors = colors(); diff --git a/packages/core/tests/safety.rs b/packages/core/tests/safety.rs index 5fc6cda7e..ddf384d6f 100644 --- a/packages/core/tests/safety.rs +++ b/packages/core/tests/safety.rs @@ -5,13 +5,15 @@ use dioxus::prelude::*; /// Ensure no issues with not calling rebuild_to_vec #[test] fn root_node_isnt_null() { - let dom = VirtualDom::new(|cx| render!("Hello world!")); + let dom = VirtualDom::new(|| render!("Hello world!")); let scope = dom.base_scope(); // We haven't built the tree, so trying to get out the root node should fail assert!(scope.try_root_node().is_none()); - // The height should be 0 - assert_eq!(scope.height(), 0); + dom.in_runtime(|| { + // The height should be 0 + assert_eq!(ScopeId::ROOT.height(), 0); + }); } diff --git a/packages/core/tests/task.rs b/packages/core/tests/task.rs index f61c45807..71b08f68d 100644 --- a/packages/core/tests/task.rs +++ b/packages/core/tests/task.rs @@ -9,15 +9,15 @@ async fn it_works() { static POLL_COUNT: AtomicUsize = AtomicUsize::new(0); fn app() -> Element { - cx.use_hook(|| { - cx.spawn(async { + once(|| { + spawn(async { for x in 0..10 { tokio::time::sleep(Duration::from_micros(50)).await; POLL_COUNT.fetch_add(x, std::sync::atomic::Ordering::Relaxed); } }); - cx.spawn(async { + spawn(async { for x in 0..10 { tokio::time::sleep(Duration::from_micros(25)).await; POLL_COUNT.fetch_add(x * 2, std::sync::atomic::Ordering::Relaxed);