diff --git a/examples/core/async.rs b/examples/core/async.rs index 24a4f34f..a21f8265 100644 --- a/examples/core/async.rs +++ b/examples/core/async.rs @@ -5,7 +5,7 @@ fn main() { dom.rebuild(); } -const App: FC<()> = |(cx, props)| { +const App: FC<()> = |cx, props| { let id = cx.scope_id(); // cx.submit_task(Box::pin(async move { id })); diff --git a/examples/core/contextapi.rs b/examples/core/contextapi.rs index c9f839f4..6c90a60c 100644 --- a/examples/core/contextapi.rs +++ b/examples/core/contextapi.rs @@ -6,7 +6,7 @@ struct SomeContext { } #[allow(unused)] -static Example: FC<()> = |(cx, props)| { +static Example: FC<()> = |cx, props| { todo!() // let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap()); diff --git a/examples/core/syntax.rs b/examples/core/syntax.rs index bda8c8f4..5eec9d18 100644 --- a/examples/core/syntax.rs +++ b/examples/core/syntax.rs @@ -33,7 +33,7 @@ fn html_usage() { static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!")); -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let name = cx.use_state(|| 0); cx.render(rsx!(div { @@ -99,7 +99,7 @@ impl<'a> Children<'a> { } } -static Bapp: FC<()> = |(cx, props)| { +static Bapp: FC<()> = |cx, props| { let name = cx.use_state(|| 0); cx.render(rsx!( @@ -114,7 +114,7 @@ static Bapp: FC<()> = |(cx, props)| { )) }; -static Match: FC<()> = |(cx, props)| { +static Match: FC<()> = |cx, props| { // let b: Box VNode> = Box::new(|f| todo!()); diff --git a/examples/core/vdom_usage.rs b/examples/core/vdom_usage.rs index 8f5d278b..cbe3c324 100644 --- a/examples/core/vdom_usage.rs +++ b/examples/core/vdom_usage.rs @@ -5,7 +5,7 @@ use dioxus_core::{lazynodes::LazyNodes, prelude::*}; // #[async_std::main] fn main() { static App: FC<()> = - |(cx, props)| cx.render(Some(LazyNodes::new(move |f| f.text(format_args!("hello"))))); + |cx, props| cx.render(Some(LazyNodes::new(move |f| f.text(format_args!("hello"))))); let mut dom = VirtualDom::new(App); diff --git a/examples/core_reference/antipatterns.rs b/examples/core_reference/antipatterns.rs index 522f99a7..735a5710 100644 --- a/examples/core_reference/antipatterns.rs +++ b/examples/core_reference/antipatterns.rs @@ -32,7 +32,7 @@ use dioxus::prelude::*; struct NoKeysProps { data: std::collections::HashMap, } -static AntipatternNoKeys: FC = |(cx, props)| { +static AntipatternNoKeys: FC = |cx, props| { // WRONG: Make sure to add keys! rsx!(cx, ul { {props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))} @@ -54,7 +54,7 @@ static AntipatternNoKeys: FC = |(cx, props)| { /// /// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing /// an API for registering shared state without the ContextProvider pattern. -static AntipatternNestedFragments: FC<()> = |(cx, props)| { +static AntipatternNestedFragments: FC<()> = |cx, props| { // Try to avoid heavily nesting fragments rsx!(cx, Fragment { @@ -82,7 +82,7 @@ static AntipatternNestedFragments: FC<()> = |(cx, props)| { /// However, calling set_state will *not* update the current version of state in the component. This should be easy to /// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state` /// merely places a new value in the queue and schedules the component for a future update. -static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| { +static AntipatternRelyingOnSetState: FC<()> = |cx, props| { let (state, set_state) = use_state(cx, || "Hello world").classic(); set_state("New state"); // This will return false! `state` will *still* be "Hello world" @@ -99,7 +99,7 @@ static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| { /// - All components must start with an uppercase character /// /// i.e.: the following component will be rejected when attempted to be used in the rsx! macro -static antipattern_component: FC<()> = |(cx, props)| todo!(); +static antipattern_component: FC<()> = |cx, props| todo!(); /// Antipattern: Misusing hooks /// --------------------------- @@ -120,7 +120,7 @@ static antipattern_component: FC<()> = |(cx, props)| todo!(); struct MisuedHooksProps { should_render_state: bool, } -static AntipatternMisusedHooks: FC = |(cx, props)| { +static AntipatternMisusedHooks: FC = |cx, props| { if props.should_render_state { // do not place a hook in the conditional! // prefer to move it out of the conditional @@ -153,7 +153,7 @@ static AntipatternMisusedHooks: FC = |(cx, props)| { /// } /// } /// }) -static _example: FC<()> = |(cx, props)| todo!(); +static _example: FC<()> = |cx, props| todo!(); /// Antipattern: publishing components and hooks with all features enabled /// ---------------------------------------------------------------------- @@ -171,9 +171,9 @@ static _example: FC<()> = |(cx, props)| todo!(); /// /// This will only include the `core` dioxus crate which is relatively slim and fast to compile and avoids target-specific /// libraries. -static __example: FC<()> = |(cx, props)| todo!(); +static __example: FC<()> = |cx, props| todo!(); -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { AntipatternNoKeys { data: std::collections::HashMap::new() } AntipatternNestedFragments {} diff --git a/examples/core_reference/basics.rs b/examples/core_reference/basics.rs index 81a252cb..6a3807bb 100644 --- a/examples/core_reference/basics.rs +++ b/examples/core_reference/basics.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { div { Greeting { @@ -25,7 +25,7 @@ struct GreetingProps { name: &'static str, } -static Greeting: FC = |(cx, props)| { +static Greeting: FC = |cx, props| { cx.render(rsx! { div { h1 { "Hello, {props.name}!" } diff --git a/examples/core_reference/children.rs b/examples/core_reference/children.rs index 0340e2dc..bdd3108d 100644 --- a/examples/core_reference/children.rs +++ b/examples/core_reference/children.rs @@ -18,7 +18,7 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { div { Banner { @@ -31,7 +31,7 @@ pub static Example: FC<()> = |(cx, props)| { }) }; -pub static Banner: FC<()> = |(cx, props)| { +pub static Banner: FC<()> = |cx, props| { cx.render(rsx! { div { h1 { "This is a great banner!" } diff --git a/examples/core_reference/conditional_rendering.rs b/examples/core_reference/conditional_rendering.rs index b3086926..6a5fe9a6 100644 --- a/examples/core_reference/conditional_rendering.rs +++ b/examples/core_reference/conditional_rendering.rs @@ -16,7 +16,7 @@ use dioxus::prelude::*; pub struct MyProps { should_show: bool, } -pub static Example0: FC = |(cx, props)| { +pub static Example0: FC = |cx, props| { cx.render(rsx! { div { {props.should_show.then(|| rsx!{ @@ -39,7 +39,7 @@ pub static Example0: FC = |(cx, props)| { pub struct MyProps1 { should_show: bool, } -pub static Example1: FC = |(cx, props)| { +pub static Example1: FC = |cx, props| { cx.render(rsx! { div { // With matching @@ -77,7 +77,7 @@ pub enum Color { pub struct MyProps2 { color: Color, } -pub static Example2: FC = |(cx, props)| { +pub static Example2: FC = |cx, props| { cx.render(rsx! { div { {match props.color { @@ -89,7 +89,7 @@ pub static Example2: FC = |(cx, props)| { }) }; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { let should_show = use_state(cx, || false); let mut color_index = use_state(cx, || 0); let color = match *color_index % 2 { diff --git a/examples/core_reference/controlled_inputs.rs b/examples/core_reference/controlled_inputs.rs index b4c056b6..bdabe83d 100644 --- a/examples/core_reference/controlled_inputs.rs +++ b/examples/core_reference/controlled_inputs.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() {} -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { div { @@ -10,7 +10,7 @@ pub static Example: FC<()> = |(cx, props)| { }; // A controlled component: -static ControlledSelect: FC<()> = |(cx, props)| { +static ControlledSelect: FC<()> = |cx, props| { let value = use_state(cx, || String::from("Grapefruit")); cx.render(rsx! { select { value: "{value}", onchange: move |evt| value.set(evt.value()), @@ -23,7 +23,7 @@ static ControlledSelect: FC<()> = |(cx, props)| { }; // TODO - how do uncontrolled things work? -static UncontrolledSelect: FC<()> = |(cx, props)| { +static UncontrolledSelect: FC<()> = |cx, props| { let value = use_state(cx, || String::new()); cx.render(rsx! { diff --git a/examples/core_reference/custom_elements.rs b/examples/core_reference/custom_elements.rs index 090dabd5..053b3aea 100644 --- a/examples/core_reference/custom_elements.rs +++ b/examples/core_reference/custom_elements.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { div { custom_element { diff --git a/examples/core_reference/empty.rs b/examples/core_reference/empty.rs index 52bc8064..28914bd6 100644 --- a/examples/core_reference/empty.rs +++ b/examples/core_reference/empty.rs @@ -5,4 +5,4 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| cx.render(rsx! { Fragment {} }); +pub static Example: FC<()> = |cx, props| cx.render(rsx! { Fragment {} }); diff --git a/examples/core_reference/errorhandling.rs b/examples/core_reference/errorhandling.rs index dd34a5f3..e598726e 100644 --- a/examples/core_reference/errorhandling.rs +++ b/examples/core_reference/errorhandling.rs @@ -23,14 +23,14 @@ fn main() {} /// This is one way to go about error handling (just toss things away with unwrap). /// However, if you get it wrong, the whole app will crash. /// This is pretty flimsy. -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let data = get_data().unwrap(); cx.render(rsx!( div { "{data}" } )) }; /// This is a pretty verbose way of error handling /// However, it's still pretty good since we don't panic, just fail to render anything -static App1: FC<()> = |(cx, props)| { +static App1: FC<()> = |cx, props| { let data = match get_data() { Some(data) => data, None => return None, @@ -46,7 +46,7 @@ static App1: FC<()> = |(cx, props)| { /// a user is logged in. /// /// Dioxus will throw an error in the console if the None-path is ever taken. -static App2: FC<()> = |(cx, props)| { +static App2: FC<()> = |cx, props| { let data = get_data()?; cx.render(rsx!( div { "{data}" } )) }; @@ -54,14 +54,14 @@ static App2: FC<()> = |(cx, props)| { /// This is top-tier error handling since it displays a failure state. /// /// However, the error is lacking in context. -static App3: FC<()> = |(cx, props)| match get_data() { +static App3: FC<()> = |cx, props| match get_data() { Some(data) => cx.render(rsx!( div { "{data}" } )), None => cx.render(rsx!( div { "Failed to load data :(" } )), }; /// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts /// a Result into an Option and lets you abort rendering by early-returning `None` -static App4: FC<()> = |(cx, props)| { +static App4: FC<()> = |cx, props| { let data = get_data_err().ok()?; cx.render(rsx!( div { "{data}" } )) }; @@ -69,7 +69,7 @@ static App4: FC<()> = |(cx, props)| { /// This is great error handling since it displays a failure state... with context! /// /// Hopefully you'll never need to display a screen like this. It's rather bad taste -static App5: FC<()> = |(cx, props)| match get_data_err() { +static App5: FC<()> = |cx, props| match get_data_err() { Ok(data) => cx.render(rsx!( div { "{data}" } )), Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )), }; diff --git a/examples/core_reference/fragments.rs b/examples/core_reference/fragments.rs index af888d02..88c9dcae 100644 --- a/examples/core_reference/fragments.rs +++ b/examples/core_reference/fragments.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; // Returning multiple elements with rsx! or html! -static App1: FC<()> = |(cx, props)| { +static App1: FC<()> = |cx, props| { cx.render(rsx! { h1 { } h2 { } @@ -20,7 +20,7 @@ static App1: FC<()> = |(cx, props)| { }; // Using the Fragment component -static App2: FC<()> = |(cx, props)| { +static App2: FC<()> = |cx, props| { cx.render(rsx! { Fragment { div {} @@ -31,7 +31,7 @@ static App2: FC<()> = |(cx, props)| { }; // Using the `fragment` method on the NodeFactory -static App3: FC<()> = |(cx, props)| { +static App3: FC<()> = |cx, props| { cx.render(LazyNodes::new(move |fac| { fac.fragment_from_iter([ fac.text(format_args!("A")), @@ -42,7 +42,7 @@ static App3: FC<()> = |(cx, props)| { })) }; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { App1 {} App2 {} diff --git a/examples/core_reference/global_css.rs b/examples/core_reference/global_css.rs index 67f3c2bb..5e7c75a8 100644 --- a/examples/core_reference/global_css.rs +++ b/examples/core_reference/global_css.rs @@ -19,7 +19,7 @@ h1 {color: blue;} p {color: red;} "#; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { head { style { "{STYLE}" } } body { diff --git a/examples/core_reference/inline_styles.rs b/examples/core_reference/inline_styles.rs index 2161812f..e3a45154 100644 --- a/examples/core_reference/inline_styles.rs +++ b/examples/core_reference/inline_styles.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { head { style: { background_color: "powderblue" } @@ -29,7 +29,7 @@ pub static Example: FC<()> = |(cx, props)| { // .... technically the rsx! macro is slightly broken at the moment and allows styles not wrapped in style {} // I haven't noticed any name collisions yet, and am tentatively leaving this behavior in.. // Don't rely on it. -static Example2: FC<()> = |(cx, props)| { +static Example2: FC<()> = |cx, props| { cx.render(rsx! { div { color: "red" "hello world!" diff --git a/examples/core_reference/iterators.rs b/examples/core_reference/iterators.rs index cd216401..f8feae8a 100644 --- a/examples/core_reference/iterators.rs +++ b/examples/core_reference/iterators.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { let example_data = use_state(cx, || 0); let v = (0..10).map(|f| { diff --git a/examples/core_reference/listener.rs b/examples/core_reference/listener.rs index 3cd13edf..63813e42 100644 --- a/examples/core_reference/listener.rs +++ b/examples/core_reference/listener.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { ButtonList {} NonUpdatingEvents {} @@ -16,7 +16,7 @@ pub static Example: FC<()> = |(cx, props)| { }; /// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name. -static ButtonList: FC<()> = |(cx, props)| { +static ButtonList: FC<()> = |cx, props| { let name = use_state(cx, || "...?"); let names = ["jack", "jill", "john", "jane"] @@ -33,7 +33,7 @@ static ButtonList: FC<()> = |(cx, props)| { /// This shows how listeners may be without a visible change in the display. /// Check the console. -static NonUpdatingEvents: FC<()> = |(cx, props)| { +static NonUpdatingEvents: FC<()> = |cx, props| { rsx!(cx, div { button { onclick: move |_| log::info!("Did not cause any updates!") @@ -42,7 +42,7 @@ static NonUpdatingEvents: FC<()> = |(cx, props)| { }) }; -static DisablePropagation: FC<()> = |(cx, props)| { +static DisablePropagation: FC<()> = |cx, props| { rsx!(cx, div { onclick: move |_| log::info!("event propagated to the div!") diff --git a/examples/core_reference/memo.rs b/examples/core_reference/memo.rs index 1532b5ab..40aa5419 100644 --- a/examples/core_reference/memo.rs +++ b/examples/core_reference/memo.rs @@ -21,7 +21,7 @@ use dioxus::prelude::*; // By default, components with no props are always memoized. // A props of () is considered empty. -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { div { "100% memoized!" } }) @@ -35,7 +35,7 @@ pub struct MyProps1 { name: String, } -pub static Example1: FC = |(cx, props)| { +pub static Example1: FC = |cx, props| { cx.render(rsx! { div { "100% memoized! {props.name}" } }) @@ -49,7 +49,7 @@ pub struct MyProps2 { name: std::rc::Rc, } -pub static Example2: FC = |(cx, props)| { +pub static Example2: FC = |cx, props| { cx.render(rsx! { div { "100% memoized! {props.name}" } }) diff --git a/examples/core_reference/noderefs.rs b/examples/core_reference/noderefs.rs index 021fcfa5..8d22ee5b 100644 --- a/examples/core_reference/noderefs.rs +++ b/examples/core_reference/noderefs.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() {} -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { let p = 10; cx.render(rsx! { diff --git a/examples/core_reference/signals.rs b/examples/core_reference/signals.rs index 74228500..2e69d8e6 100644 --- a/examples/core_reference/signals.rs +++ b/examples/core_reference/signals.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() {} -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { div { diff --git a/examples/core_reference/spreadpattern.rs b/examples/core_reference/spreadpattern.rs index 56bde5ae..58a813cb 100644 --- a/examples/core_reference/spreadpattern.rs +++ b/examples/core_reference/spreadpattern.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { let props = MyProps { count: 0, live: true, diff --git a/examples/core_reference/statemanagement.rs b/examples/core_reference/statemanagement.rs index 74228500..2e69d8e6 100644 --- a/examples/core_reference/statemanagement.rs +++ b/examples/core_reference/statemanagement.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() {} -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { div { diff --git a/examples/core_reference/suspense.rs b/examples/core_reference/suspense.rs index 2a89e97e..e3bbd257 100644 --- a/examples/core_reference/suspense.rs +++ b/examples/core_reference/suspense.rs @@ -14,7 +14,7 @@ struct DogApi { } const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random"; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { let doggo = use_suspense( cx, || surf::get(ENDPOINT).recv_json::(), diff --git a/examples/core_reference/task.rs b/examples/core_reference/task.rs index cb70881d..a5e877bc 100644 --- a/examples/core_reference/task.rs +++ b/examples/core_reference/task.rs @@ -24,7 +24,7 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { let count = use_state(cx, || 0); let mut direction = use_state(cx, || 1); diff --git a/examples/core_reference/testing.rs b/examples/core_reference/testing.rs index 4ce33c5f..587ce19c 100644 --- a/examples/core_reference/testing.rs +++ b/examples/core_reference/testing.rs @@ -1,6 +1,6 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { cx.render(rsx! { div { diff --git a/examples/core_reference/tostring.rs b/examples/core_reference/tostring.rs index dd10c658..a04a165d 100644 --- a/examples/core_reference/tostring.rs +++ b/examples/core_reference/tostring.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; use dioxus::ssr; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { let as_string = use_state(cx, || { // Currently, SSR is only supported for whole VirtualDOMs // This is an easy/low hanging fruit to improve upon @@ -15,7 +15,7 @@ pub static Example: FC<()> = |(cx, props)| { }) }; -static SomeApp: FC<()> = |(cx, props)| { +static SomeApp: FC<()> = |cx, props| { cx.render(rsx! { div { style: {background_color: "blue"} h1 {"Some amazing app or component"} diff --git a/examples/coroutine.rs b/examples/coroutine.rs index 61b06502..c2e025e1 100644 --- a/examples/coroutine.rs +++ b/examples/coroutine.rs @@ -27,7 +27,7 @@ fn main() { use dioxus::prelude::*; -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let p1 = use_state(cx, || 0); let p2 = use_state(cx, || 0); diff --git a/examples/desktop/demo.rs b/examples/desktop/demo.rs index 2b3367b2..77a927e9 100644 --- a/examples/desktop/demo.rs +++ b/examples/desktop/demo.rs @@ -8,7 +8,7 @@ fn main() { dioxus_desktop::launch(App, |c| c); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { cx.render(rsx!( div { "hello world!" diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 096922a2..113f5221 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -18,7 +18,7 @@ fn main() { }); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let file_manager = use_ref(cx, || Files::new()); let files = file_manager.read(); diff --git a/examples/hydration.rs b/examples/hydration.rs index 494b6654..f50f740a 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -19,7 +19,7 @@ fn main() { dioxus::desktop::launch(App, |c| c.with_prerendered(content)); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let mut val = use_state(cx, || 0); cx.render(rsx! { diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index 0accd170..d20477e8 100644 --- a/examples/pattern_model.rs +++ b/examples/pattern_model.rs @@ -31,7 +31,7 @@ fn main() { }); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let state = use_ref(cx, || Calculator::new()); let clear_display = state.read().display_value.eq("0"); diff --git a/examples/readme.rs b/examples/readme.rs index b1e29a98..8eb8b098 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -7,7 +7,7 @@ fn main() { dioxus::desktop::launch(App, |c| c); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let mut count = use_state(cx, || 0); cx.render(rsx! { diff --git a/examples/router.rs b/examples/router.rs index e04f92eb..5b021a80 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -23,7 +23,7 @@ pub enum Route { NotFound, } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let route = use_router(cx, Route::parse)?; cx.render(rsx! { diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index d14e1fec..c0d69535 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None; use baller::Baller; use dioxus::prelude::*; -pub static Example: FC<()> = |(cx, props)| { +pub static Example: FC<()> = |cx, props| { let formatting = "formatting!"; let formatting_tuple = ("a", "b"); let lazy_fmt = format_args!("lazily formatted text"); diff --git a/examples/ssr.rs b/examples/ssr.rs index 9c30d1a2..539db865 100644 --- a/examples/ssr.rs +++ b/examples/ssr.rs @@ -9,7 +9,7 @@ fn main() { println!("{}", ssr::render_vdom(&vdom, |c| c)); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { cx.render(rsx!( div { h1 { "Title" } diff --git a/examples/ssr/basic.rs b/examples/ssr/basic.rs index a4be3a70..c89c425e 100644 --- a/examples/ssr/basic.rs +++ b/examples/ssr/basic.rs @@ -12,7 +12,7 @@ fn main() { ) } -pub static App: FC<()> = |(cx, props)| { +pub static App: FC<()> = |cx, props| { cx.render(rsx!( div { class: "overflow-hidden" diff --git a/examples/ssr/tide.rs b/examples/ssr/tide.rs index eada5dae..d86fbdf5 100644 --- a/examples/ssr/tide.rs +++ b/examples/ssr/tide.rs @@ -42,7 +42,7 @@ fn main() {} // initial_name: String, // } -// static Example: FC = |(cx, props)| { +// static Example: FC = |cx, props| { // let dispaly_name = use_state(cx, move || props.initial_name.clone()); // cx.render(rsx! { diff --git a/examples/ssr/tofile.rs b/examples/ssr/tofile.rs index 35777240..e3091580 100644 --- a/examples/ssr/tofile.rs +++ b/examples/ssr/tofile.rs @@ -24,7 +24,7 @@ fn main() { .unwrap(); } -pub static App: FC<()> = |(cx, props)| { +pub static App: FC<()> = |cx, props| { cx.render(rsx!( div { class: "overflow-hidden" link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" } @@ -39,7 +39,7 @@ pub static App: FC<()> = |(cx, props)| { )) }; -pub static Header: FC<()> = |(cx, props)| { +pub static Header: FC<()> = |cx, props| { cx.render(rsx! { div { header { class: "text-gray-400 bg-gray-900 body-font" @@ -65,7 +65,7 @@ pub static Header: FC<()> = |(cx, props)| { }) }; -pub static Hero: FC<()> = |(cx, props)| { +pub static Hero: FC<()> = |cx, props| { // cx.render(rsx! { section{ class: "text-gray-400 bg-gray-900 body-font" @@ -103,7 +103,7 @@ pub static Hero: FC<()> = |(cx, props)| { } }) }; -pub static Entry: FC<()> = |(cx, props)| { +pub static Entry: FC<()> = |cx, props| { // cx.render(rsx! { section{ class: "text-gray-400 bg-gray-900 body-font" @@ -116,7 +116,7 @@ pub static Entry: FC<()> = |(cx, props)| { }) }; -pub static StacksIcon: FC<()> = |(cx, props)| { +pub static StacksIcon: FC<()> = |cx, props| { cx.render(rsx!( svg { xmlns: "http://www.w3.org/2000/svg" @@ -131,7 +131,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| { } )) }; -pub static RightArrowIcon: FC<()> = |(cx, props)| { +pub static RightArrowIcon: FC<()> = |cx, props| { cx.render(rsx!( svg { fill: "none" diff --git a/examples/tailwind.rs b/examples/tailwind.rs index 47046be8..fe710076 100644 --- a/examples/tailwind.rs +++ b/examples/tailwind.rs @@ -14,7 +14,7 @@ fn main() { const STYLE: &str = "body {overflow:hidden;}"; -pub static App: FC<()> = |(cx, props)| { +pub static App: FC<()> = |cx, props| { cx.render(rsx!( div { class: "overflow-hidden" style { "{STYLE}" } @@ -30,7 +30,7 @@ pub static App: FC<()> = |(cx, props)| { )) }; -pub static Header: FC<()> = |(cx, props)| { +pub static Header: FC<()> = |cx, props| { cx.render(rsx! { div { header { class: "text-gray-400 bg-gray-900 body-font" @@ -56,7 +56,7 @@ pub static Header: FC<()> = |(cx, props)| { }) }; -pub static Hero: FC<()> = |(cx, props)| { +pub static Hero: FC<()> = |cx, props| { // cx.render(rsx! { section{ class: "text-gray-400 bg-gray-900 body-font" @@ -94,7 +94,7 @@ pub static Hero: FC<()> = |(cx, props)| { } }) }; -pub static Entry: FC<()> = |(cx, props)| { +pub static Entry: FC<()> = |cx, props| { // cx.render(rsx! { section{ class: "text-gray-400 bg-gray-900 body-font" @@ -107,7 +107,7 @@ pub static Entry: FC<()> = |(cx, props)| { }) }; -pub static StacksIcon: FC<()> = |(cx, props)| { +pub static StacksIcon: FC<()> = |cx, props| { cx.render(rsx!( svg { // xmlns: "http://www.w3.org/2000/svg" @@ -122,7 +122,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| { } )) }; -pub static RightArrowIcon: FC<()> = |(cx, props)| { +pub static RightArrowIcon: FC<()> = |cx, props| { cx.render(rsx!( svg { fill: "none" diff --git a/examples/tasks.rs b/examples/tasks.rs new file mode 100644 index 00000000..a7daca83 --- /dev/null +++ b/examples/tasks.rs @@ -0,0 +1,23 @@ +//! Example: README.md showcase +//! +//! The example from the README.md. + +use dioxus::prelude::*; +fn main() { + dioxus::desktop::launch(App, |c| c); +} + +static App: FC<()> = |cx, props| { + let mut count = use_state(cx, || 0); + + cx.push_task(async { + panic!("polled future"); + // + }); + + cx.render(rsx! { + div { + h1 { "High-Five counter: {count}" } + } + }) +}; diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 6ed2c1d7..cfc49f53 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -22,7 +22,7 @@ pub struct TodoItem { } const STYLE: &str = include_str!("./assets/todomvc.css"); -const App: FC<()> = |(cx, props)| { +const App: FC<()> = |cx, props| { let mut draft = use_state(cx, || "".to_string()); let mut todos = use_state(cx, || HashMap::>::new()); let mut filter = use_state(cx, || FilterState::All); @@ -85,7 +85,7 @@ pub struct TodoEntryProps { todo: Rc, } -pub fn TodoEntry((cx, props): Scope) -> Element { +pub fn TodoEntry(cx: Context, 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/weather_app.rs b/examples/weather_app.rs index 5d77bea1..fca6c8cf 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -12,7 +12,7 @@ fn main() { const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather"; -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { // let body = use_suspense( cx, @@ -40,7 +40,7 @@ static App: FC<()> = |(cx, props)| { #[derive(PartialEq, Props)] struct WeatherProps {} -static WeatherDisplay: FC = |(cx, props)| { +static WeatherDisplay: FC = |cx, props| { // cx.render(rsx!( div { class: "flex items-center justify-center flex-col" diff --git a/examples/web/blah.rs b/examples/web/blah.rs index de4bf3cd..d21cd5fd 100644 --- a/examples/web/blah.rs +++ b/examples/web/blah.rs @@ -24,7 +24,7 @@ fn main() { dioxus_web::launch(App, |c| c) } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let mut state = use_state(cx, || 0); cx.render(rsx! { div { diff --git a/examples/web/btns.rs b/examples/web/btns.rs index 59db81f3..179074ee 100644 --- a/examples/web/btns.rs +++ b/examples/web/btns.rs @@ -23,7 +23,7 @@ fn main() { // dioxus::web::launch(App, |c| c); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { dbg!("rednering parent"); cx.render(rsx! { div { @@ -40,7 +40,7 @@ static App: FC<()> = |(cx, props)| { }) }; -static But: FC<()> = |(cx, props)| { +static But: FC<()> = |cx, props| { let mut count = use_state(cx, || 0); // let d = Dropper { name: "asd" }; diff --git a/examples/web/demo.rs b/examples/web/demo.rs index f331560e..603b84de 100644 --- a/examples/web/demo.rs +++ b/examples/web/demo.rs @@ -24,7 +24,7 @@ fn main() { dioxus_web::launch(App, |c| c) } -pub static App: FC<()> = |(cx, props)| { +pub static App: FC<()> = |cx, props| { cx.render(rsx!( div { class: "overflow-hidden" link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" } @@ -39,7 +39,7 @@ pub static App: FC<()> = |(cx, props)| { )) }; -pub static Header: FC<()> = |(cx, props)| { +pub static Header: FC<()> = |cx, props| { cx.render(rsx! { div { header { class: "text-gray-400 bg-gray-900 body-font" @@ -65,7 +65,7 @@ pub static Header: FC<()> = |(cx, props)| { }) }; -pub static Hero: FC<()> = |(cx, props)| { +pub static Hero: FC<()> = |cx, props| { // cx.render(rsx! { section{ class: "text-gray-400 bg-gray-900 body-font" @@ -103,7 +103,7 @@ pub static Hero: FC<()> = |(cx, props)| { } }) }; -pub static Entry: FC<()> = |(cx, props)| { +pub static Entry: FC<()> = |cx, props| { // cx.render(rsx! { section{ class: "text-gray-400 bg-gray-900 body-font" @@ -116,7 +116,7 @@ pub static Entry: FC<()> = |(cx, props)| { }) }; -pub static StacksIcon: FC<()> = |(cx, props)| { +pub static StacksIcon: FC<()> = |cx, props| { cx.render(rsx!( svg { // xmlns: "http://www.w3.org/2000/svg" @@ -131,7 +131,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| { } )) }; -pub static RightArrowIcon: FC<()> = |(cx, props)| { +pub static RightArrowIcon: FC<()> = |cx, props| { cx.render(rsx!( svg { fill: "none" diff --git a/examples/web_tick.rs b/examples/web_tick.rs index ea2e087f..1e60af99 100644 --- a/examples/web_tick.rs +++ b/examples/web_tick.rs @@ -19,7 +19,7 @@ fn main() { dioxus::web::launch(App, |c| c); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let mut rng = SmallRng::from_entropy(); let rows = (0..1_000).map(|f| { let label = Label::new(&mut rng); diff --git a/examples/webview_web.rs b/examples/webview_web.rs index 408095c5..debe4d8e 100644 --- a/examples/webview_web.rs +++ b/examples/webview_web.rs @@ -16,7 +16,7 @@ fn main() { dioxus::web::launch(App, |c| c); } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let mut count = use_state(cx, || 0); cx.render(rsx! { diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs index c65fc9ee..23ff3b53 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/packages/core-macro/src/rsx/component.rs @@ -173,7 +173,7 @@ impl ToTokens for Component { if !self.children.is_empty() { let childs = &self.children; toks.append_all(quote! { - .children(ScopeChildren::new(__cx.fragment_from_iter([ #( #childs ),* ]))) + .children(__cx.create_children([ #( #childs ),* ])) }); } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 7a3bd4b4..e87157c8 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -321,14 +321,15 @@ impl<'bump> DiffState<'bump> { } MountType::Replace { old } => { + // todo: a bug here where we remove a node that is alread being replaced if let Some(old_id) = old.try_mounted_id() { self.mutations.replace_with(old_id, nodes_created as u32); - self.remove_nodes(Some(old), true); + self.remove_nodes(Some(old), false); } else { if let Some(id) = self.find_first_element_id(old) { self.mutations.replace_with(id, nodes_created as u32); } - self.remove_nodes(Some(old), true); + self.remove_nodes(Some(old), false); } } @@ -487,7 +488,11 @@ impl<'bump> DiffState<'bump> { } fn create_linked_node(&mut self, link: &'bump NodeLink) { - todo!() + 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) }); + } } // ================================= @@ -532,7 +537,6 @@ impl<'bump> DiffState<'bump> { fn diff_element_nodes( &mut self, - old: &'bump VElement<'bump>, new: &'bump VElement<'bump>, old_node: &'bump VNode<'bump>, @@ -629,10 +633,8 @@ impl<'bump> DiffState<'bump> { fn diff_component_nodes( &mut self, - old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>, - old: &'bump VComponent<'bump>, new: &'bump VComponent<'bump>, ) { @@ -648,21 +650,20 @@ impl<'bump> DiffState<'bump> { new.associated_scope.set(Some(scope_addr)); // make sure the component's caller function is up to date - let scope = self.scopes.get_scope(&scope_addr).unwrap(); - let mut items = scope.items.borrow_mut(); + let scope = unsafe { self.scopes.get_scope_mut(&scope_addr).unwrap() }; + scope.caller = unsafe { std::mem::transmute(new.caller) }; // React doesn't automatically memoize, but we do. - let props_are_the_same = todo!("reworking component memoization"); - // let props_are_the_same = todo!("reworking component memoization"); - // let props_are_the_same = old.comparator.unwrap(); + let props_are_the_same = old.comparator.unwrap(); - // if self.cfg.force_diff || !props_are_the_same(new) { - // let succeeded = scope.run_scope(self); - - // if succeeded { - // self.diff_node(scope.frames.wip_head(), scope.frames.fin_head()); - // } - // } + if self.force_diff || !props_are_the_same(new) { + if self.scopes.run_scope(&scope_addr) { + self.diff_node( + self.scopes.wip_head(&scope_addr), + self.scopes.fin_head(&scope_addr), + ); + } + } self.stack.scope_stack.pop(); } else { @@ -1198,7 +1199,6 @@ impl<'bump> DiffState<'bump> { /// remove can happen whenever fn remove_nodes( &mut self, - nodes: impl IntoIterator>, gen_muts: bool, ) { @@ -1244,18 +1244,16 @@ impl<'bump> DiffState<'bump> { } VNode::Linked(l) => { - todo!() + let node = unsafe { std::mem::transmute(&*l.node) }; + self.remove_nodes(Some(node), gen_muts); } VNode::Component(c) => { let scope_id = c.associated_scope.get().unwrap(); - // let scope = self.scopes.get_scope(&scope_id).unwrap(); let root = self.scopes.root_node(&scope_id); self.remove_nodes(Some(root), gen_muts); - log::debug!("Destroying scope {:?}", scope_id); - let mut s = self.scopes.try_remove(&scope_id).unwrap(); - s.hooks.clear_hooks(); + self.scopes.try_remove(&scope_id).unwrap(); } } } diff --git a/packages/core/src/lazynodes.rs b/packages/core/src/lazynodes.rs index 72bb8dfb..f9b236a2 100644 --- a/packages/core/src/lazynodes.rs +++ b/packages/core/src/lazynodes.rs @@ -245,7 +245,7 @@ fn it_drops() { simple_logger::init().unwrap(); - let factory = NodeFactory { bump: &bump }; + // let factory = NodeFactory { scope: &bump }; struct DropInner { id: i32, diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs index ab8106b7..1686b1c0 100644 --- a/packages/core/src/mutations.rs +++ b/packages/core/src/mutations.rs @@ -3,21 +3,32 @@ //! This module contains an internal API to generate these instructions. use crate::innerlude::*; -use std::any::Any; +use std::{any::Any, fmt::Debug}; -#[derive(Debug)] pub struct Mutations<'a> { pub edits: Vec>, pub noderefs: Vec>, + pub effects: Vec<&'a dyn FnMut()>, +} +impl Debug for Mutations<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Mutations") + .field("edits", &self.edits) + .field("noderefs", &self.noderefs) + // .field("effects", &self.effects) + .finish() + } } use DomEdit::*; impl<'a> Mutations<'a> { pub(crate) fn new() -> Self { - let edits = Vec::new(); - let noderefs = Vec::new(); - Self { edits, noderefs } + Self { + edits: Vec::new(), + noderefs: Vec::new(), + effects: Vec::new(), + } } // Navigation diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 77bf0317..c5b9be26 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -181,7 +181,7 @@ impl<'src> VNode<'src> { pub(crate) fn children(&self) -> &[VNode<'src>] { match &self { VNode::Fragment(f) => f.children, - VNode::Component(c) => todo!("children are not accessible through this"), + VNode::Component(_c) => todo!("children are not accessible through this"), _ => &[], } } @@ -199,9 +199,9 @@ impl<'src> VNode<'src> { key: f.key, }), VNode::Linked(c) => VNode::Linked(NodeLink { - gen_id: c.gen_id, - scope_id: c.scope_id, - link_idx: c.link_idx, + scope_id: c.scope_id.clone(), + link_idx: c.link_idx.clone(), + node: c.node.clone(), }), } } @@ -224,11 +224,7 @@ impl Debug for VNode<'_> { } VNode::Suspended { .. } => write!(s, "VNode::VSuspended"), VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc), - VNode::Linked(c) => write!( - s, - "VNode::VCached {{ gen_id: {}, scope_id: {:?} }}", - c.gen_id, c.scope_id - ), + VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()), } } } @@ -419,9 +415,25 @@ pub struct VSuspended<'a> { /// an `rsx!` call. #[derive(Debug)] pub struct NodeLink { - pub(crate) link_idx: usize, - pub(crate) gen_id: u32, - pub(crate) scope_id: ScopeId, + pub(crate) link_idx: Cell, + pub(crate) scope_id: Cell>, + pub(crate) node: *const VNode<'static>, +} + +impl PartialEq for NodeLink { + fn eq(&self, other: &Self) -> bool { + self.node == other.node + } +} +impl NodeLink { + // we don't want to let users clone NodeLinks + pub(crate) fn clone_inner(&self) -> Self { + Self { + link_idx: self.link_idx.clone(), + scope_id: self.scope_id.clone(), + node: self.node.clone(), + } + } } /// This struct provides an ergonomic API to quickly build VNodes. @@ -440,7 +452,7 @@ impl<'a> NodeFactory<'a> { #[inline] pub fn bump(&self) -> &'a bumpalo::Bump { - self.bump + &self.bump } /// Directly pass in text blocks without the need to use the format_args macro. @@ -460,7 +472,7 @@ impl<'a> NodeFactory<'a> { Some(static_str) => (static_str, true), None => { use bumpalo::core_alloc::fmt::Write; - let mut str_buf = bumpalo::collections::String::new_in(self.bump()); + let mut str_buf = bumpalo::collections::String::new_in(self.bump); str_buf.write_fmt(args).unwrap(); (str_buf.into_bump_str(), false) } @@ -516,18 +528,18 @@ impl<'a> NodeFactory<'a> { A: 'a + AsRef<[Attribute<'a>]>, V: 'a + AsRef<[VNode<'a>]>, { - let listeners: &'a L = self.bump().alloc(listeners); + let listeners: &'a L = self.bump.alloc(listeners); let listeners = listeners.as_ref(); - let attributes: &'a A = self.bump().alloc(attributes); + let attributes: &'a A = self.bump.alloc(attributes); let attributes = attributes.as_ref(); - let children: &'a V = self.bump().alloc(children); + let children: &'a V = self.bump.alloc(children); let children = children.as_ref(); let key = key.map(|f| self.raw_text(f).0); - VNode::Element(self.bump().alloc(VElement { + VNode::Element(self.bump.alloc(VElement { tag_name, key, namespace, @@ -565,7 +577,7 @@ impl<'a> NodeFactory<'a> { where P: Properties + 'a, { - let bump = self.bump(); + let bump = self.bump; let props = bump.alloc(props); let bump_props = props as *mut P as *mut (); let user_fc = component as *const (); @@ -598,7 +610,6 @@ impl<'a> NodeFactory<'a> { let drop_props: &mut dyn FnMut() = bump.alloc_with(|| { move || unsafe { - log::debug!("dropping props!"); if !has_dropped { let real_other = bump_props as *mut _ as *mut P; let b = BumpBox::from_raw(real_other); @@ -620,7 +631,6 @@ impl<'a> NodeFactory<'a> { let caller: &'a mut dyn Fn(&'a Scope) -> Element = bump.alloc(move |scope: &Scope| -> Element { - log::debug!("calling component renderr {:?}", scope.our_arena_idx); let props: &'_ P = unsafe { &*(bump_props as *const P) }; let res = component(scope, props); res @@ -657,7 +667,7 @@ impl<'a> NodeFactory<'a> { self, node_iter: impl IntoIterator>, ) -> VNode<'a> { - let bump = self.bump(); + let bump = self.bump; let mut nodes = bumpalo::collections::Vec::new_in(bump); for node in node_iter { @@ -697,6 +707,58 @@ impl<'a> NodeFactory<'a> { }) } + // this isn't quite feasible yet + // I think we need some form of interior mutability or state on nodefactory that stores which subtree was created + pub fn create_children( + self, + node_iter: impl IntoIterator>, + ) -> Element { + let bump = self.bump; + let mut nodes = bumpalo::collections::Vec::new_in(bump); + + for node in node_iter { + nodes.push(node.into_vnode(self)); + } + + if nodes.is_empty() { + nodes.push(VNode::Anchor(bump.alloc(VAnchor { + dom_id: empty_cell(), + }))); + } + + let children = nodes.into_bump_slice(); + + // 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 { + children, + key: None, + }); + let ptr = self.bump.alloc(frag) as *const _; + Some(NodeLink { + link_idx: Default::default(), + scope_id: Default::default(), + node: unsafe { std::mem::transmute(ptr) }, + }) + } + pub fn annotate_lazy<'z, 'b>( f: impl FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b, ) -> Option> { @@ -796,28 +858,48 @@ impl IntoVNode<'_> for Arguments<'_> { } } +// called cx.render from a helper function impl IntoVNode<'_> for Option { - fn into_vnode(self, cx: NodeFactory) -> VNode { - todo!() + fn into_vnode(self, _cx: NodeFactory) -> VNode { + match self { + Some(node) => VNode::Linked(node), + None => { + todo!() + } + } } } +// essentially passing elements through props +// just build a new element in place impl IntoVNode<'_> for &Option { - fn into_vnode(self, cx: NodeFactory) -> VNode { - todo!() + fn into_vnode(self, _cx: NodeFactory) -> VNode { + match self { + Some(node) => VNode::Linked(NodeLink { + link_idx: node.link_idx.clone(), + scope_id: node.scope_id.clone(), + node: node.node, + }), + None => { + // + todo!() + } + } } } -// Conveniently, we also support "None" impl IntoVNode<'_> for NodeLink { - fn into_vnode(self, cx: NodeFactory) -> VNode { - todo!() + fn into_vnode(self, _cx: NodeFactory) -> VNode { + VNode::Linked(self) } } -// Conveniently, we also support "None" impl IntoVNode<'_> for &NodeLink { - fn into_vnode(self, cx: NodeFactory) -> VNode { - todo!() + fn into_vnode(self, _cx: NodeFactory) -> VNode { + VNode::Linked(NodeLink { + link_idx: self.link_idx.clone(), + scope_id: self.scope_id.clone(), + node: self.node, + }) } } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index b2072dc8..95ee9342 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -2,6 +2,7 @@ use crate::innerlude::*; use futures_channel::mpsc::UnboundedSender; use fxhash::FxHashMap; +use smallvec::SmallVec; use std::{ any::{Any, TypeId}, cell::{Cell, RefCell}, @@ -62,9 +63,6 @@ pub struct Scope { // The double-buffering situation that we will use pub(crate) frames: [BumpFrame; 2], - pub(crate) old_root: RefCell>, - pub(crate) new_root: RefCell>, - pub(crate) caller: *const dyn Fn(&Scope) -> Element, /* @@ -241,19 +239,22 @@ impl Scope { /// } ///``` pub fn render<'src>(&'src self, lazy_nodes: Option>) -> Option { - let bump = &self.wip_frame().bump; + let frame = self.wip_frame(); + let bump = &frame.bump; let factory = NodeFactory { bump }; let node = lazy_nodes.map(|f| f.call(factory))?; + let node = bump.alloc(node); - let idx = self - .wip_frame() - .add_node(unsafe { std::mem::transmute(node) }); + let node_ptr = node as *mut _; + let node_ptr = unsafe { std::mem::transmute(node_ptr) }; - Some(NodeLink { - gen_id: self.generation.get(), - scope_id: self.our_arena_idx, - link_idx: idx, - }) + let link = NodeLink { + scope_id: Cell::new(Some(self.our_arena_idx)), + link_idx: Cell::new(0), + node: node_ptr, + }; + + Some(link) } /// Push an effect to be ran after the component has been successfully mounted to the dom @@ -435,6 +436,7 @@ impl Scope { false => &self.frames[1], } } + pub(crate) fn fin_frame(&self) -> &BumpFrame { match self.generation.get() & 1 == 1 { true => &self.frames[0], @@ -496,28 +498,42 @@ impl Scope { effect(); } } + + pub fn root_node(&self) -> &VNode { + let node = *self.wip_frame().nodes.borrow().get(0).unwrap(); + unsafe { std::mem::transmute(&*node) } + } } -pub struct BumpFrame { +pub(crate) struct BumpFrame { pub bump: Bump, - pub nodes: RefCell>>, + pub nodes: RefCell>>, } impl BumpFrame { - pub fn new() -> Self { - let bump = Bump::new(); + pub fn new(capacity: usize) -> Self { + let bump = Bump::with_capacity(capacity); let node = &*bump.alloc(VText { text: "asd", dom_id: Default::default(), is_static: false, }); - let nodes = RefCell::new(vec![VNode::Text(unsafe { std::mem::transmute(node) })]); + let node = bump.alloc(VNode::Text(unsafe { std::mem::transmute(node) })); + let nodes = RefCell::new(vec![node as *const _]); Self { bump, nodes } } - fn add_node(&self, node: VNode<'static>) -> usize { + + pub fn allocated_bytes(&self) -> usize { + self.bump.allocated_bytes() + } + + pub fn assign_nodelink(&self, node: &NodeLink) { let mut nodes = self.nodes.borrow_mut(); - nodes.push(node); - nodes.len() - 1 + + let len = nodes.len(); + nodes.push(node.node); + + node.link_idx.set(len); } } @@ -531,11 +547,18 @@ impl BumpFrame { #[derive(Default)] pub(crate) struct HookList { arena: Bump, - vals: RefCell>, + vals: RefCell>, idx: Cell, } impl HookList { + pub fn new(capacity: usize) -> Self { + Self { + arena: Bump::with_capacity(capacity), + ..Default::default() + } + } + pub(crate) fn next(&self) -> Option<&mut T> { self.vals.borrow().get(self.idx.get()).and_then(|inn| { self.idx.set(self.idx.get() + 1); @@ -571,7 +594,7 @@ impl HookList { self.cur_idx() >= self.len() } - pub fn clear_hooks(&mut self) { + pub fn clear(&mut self) { self.vals.borrow_mut().drain(..).for_each(|state| { let as_mut = unsafe { &mut *state }; let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) }; diff --git a/packages/core/src/scopearena.rs b/packages/core/src/scopearena.rs index 911da1d0..1379d468 100644 --- a/packages/core/src/scopearena.rs +++ b/packages/core/src/scopearena.rs @@ -1,3 +1,4 @@ +use fxhash::FxHashMap; use slab::Slab; use std::cell::{Cell, RefCell}; @@ -7,7 +8,6 @@ use futures_channel::mpsc::UnboundedSender; use crate::innerlude::*; pub type FcSlot = *const (); -// pub heuristics: FxHashMap, pub struct Heuristic { hook_arena_size: usize, @@ -21,6 +21,7 @@ pub struct Heuristic { pub(crate) struct ScopeArena { bump: Bump, scopes: RefCell>, + pub heuristics: RefCell>, free_scopes: RefCell>, nodes: RefCell>>, pub(crate) sender: UnboundedSender, @@ -31,6 +32,7 @@ impl ScopeArena { Self { bump: Bump::new(), scopes: RefCell::new(Vec::new()), + heuristics: RefCell::new(FxHashMap::default()), free_scopes: RefCell::new(Vec::new()), nodes: RefCell::new(Slab::new()), sender, @@ -68,33 +70,53 @@ impl ScopeArena { } else { let scope_id = ScopeId(self.scopes.borrow().len()); - let old_root = NodeLink { - link_idx: 0, - gen_id: 0, - scope_id, - }; - let new_root = NodeLink { - link_idx: 0, - gen_id: 0, - scope_id, + let (node_capacity, hook_capacity) = { + let heuristics = self.heuristics.borrow(); + if let Some(heuristic) = heuristics.get(&fc_ptr) { + (heuristic.node_arena_size, heuristic.hook_arena_size) + } else { + (0, 0) + } }; - let new_scope = Scope { + 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 scope = self.bump.alloc(Scope { sender: self.sender.clone(), parent_scope, our_arena_idx: scope_id, height, subtree: Cell::new(subtree), is_subtree_root: Cell::new(false), - frames: [BumpFrame::new(), BumpFrame::new()], + frames, - hooks: Default::default(), + hooks: HookList::new(hook_capacity), shared_contexts: Default::default(), caller, generation: 0.into(), - old_root: RefCell::new(Some(old_root)), - new_root: RefCell::new(Some(new_root)), items: RefCell::new(SelfReferentialItems { listeners: Default::default(), borrowed_props: Default::default(), @@ -102,16 +124,42 @@ impl ScopeArena { tasks: Default::default(), pending_effects: Default::default(), }), - }; + }); - let stable = self.bump.alloc(new_scope); - self.scopes.borrow_mut().push(stable); + self.scopes.borrow_mut().push(scope); scope_id } } - pub fn try_remove(&self, id: &ScopeId) -> Option { - todo!() + pub fn try_remove(&self, id: &ScopeId) -> Option<()> { + self.ensure_drop_safety(id); + + let mut scope = unsafe { &mut *self.get_scope_raw(id)? }; + + // we're just reusing scopes so we need to clear it out + scope.hooks.clear(); + scope.shared_contexts.get_mut().clear(); + scope.parent_scope = None; + scope.generation.set(0); + scope.is_subtree_root.set(false); + scope.subtree.set(0); + + let SelfReferentialItems { + borrowed_props, + listeners, + pending_effects, + suspended_nodes, + tasks, + } = scope.items.get_mut(); + + borrowed_props.clear(); + listeners.clear(); + pending_effects.clear(); + suspended_nodes.clear(); + tasks.clear(); + + self.free_scopes.borrow_mut().push(*id); + Some(()) } pub fn reserve_node(&self, node: &VNode) -> ElementId { @@ -123,16 +171,10 @@ impl ScopeArena { let node = unsafe { std::mem::transmute::<*const VNode, *const VNode>(node) }; entry.insert(node); id - - // let nodes = self.nodes.borrow_mut(); - // let id = nodes.insert(()); - // let node_id = ElementId(id); - // node = Some(node_id); - // node_id } pub fn collect_garbage(&self, id: ElementId) { - todo!() + self.nodes.borrow_mut().remove(id.0); } // These methods would normally exist on `scope` but they need access to *all* of the scopes @@ -152,35 +194,25 @@ impl ScopeArena { pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) { let scope = self.get_scope(scope_id).unwrap(); + let mut items = scope.items.borrow_mut(); + // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we // run the hooks (which hold an &mut Reference) - // right now, we don't drop - scope - .items - .borrow_mut() - .borrowed_props - .drain(..) - .for_each(|comp| { - // First drop the component's undropped references - let scope_id = comp - .associated_scope - .get() - .expect("VComponents should be associated with a valid Scope"); + // recursively call ensure_drop_safety on all children + items.borrowed_props.drain(..).for_each(|comp| { + let scope_id = comp + .associated_scope + .get() + .expect("VComponents should be associated with a valid Scope"); - todo!("move this onto virtualdom"); - // let scope = unsafe { &mut *scope_id }; + self.ensure_drop_safety(&scope_id); - // scope.ensure_drop_safety(); - - todo!("drop the component's props"); - // let mut drop_props = comp.drop_props.borrow_mut().take().unwrap(); - // drop_props(); - }); + let mut drop_props = comp.drop_props.borrow_mut().take().unwrap(); + drop_props(); + }); // Now that all the references are gone, we can safely drop our own references in our listeners. - scope - .items - .borrow_mut() + items .listeners .drain(..) .for_each(|listener| drop(listener.callback.borrow_mut().take())); @@ -212,59 +244,58 @@ impl ScopeArena { // just forget about our suspended nodes while we're at it items.suspended_nodes.clear(); + items.tasks.clear(); + items.pending_effects.clear(); // guarantee that we haven't screwed up - there should be no latent references anywhere debug_assert!(items.listeners.is_empty()); - debug_assert!(items.suspended_nodes.is_empty()); debug_assert!(items.borrowed_props.is_empty()); + debug_assert!(items.suspended_nodes.is_empty()); + debug_assert!(items.tasks.is_empty()); + debug_assert!(items.pending_effects.is_empty()); - log::debug!("Borrowed stuff is successfully cleared"); - - // temporarily cast the vcomponent to the right lifetime - // let vcomp = scope.load_vcomp(); + // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. + scope.wip_frame().nodes.borrow_mut().clear(); } let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller }; - // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. - scope.wip_frame().nodes.borrow_mut().clear(); - if let Some(key) = render(scope) { - dbg!(key); + 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)); - dbg!(&scope.wip_frame().nodes.borrow_mut()); - // let mut old = scope.old_root.borrow_mut(); - // let mut new = scope.new_root.borrow_mut(); + // 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 new_old = new.clone(); - // *old = new_old; - // *new = Some(key); + if !scope.items.borrow().tasks.is_empty() { + // self. + } - // dbg!(&old); - // dbg!(&new); - - // the user's component succeeded. We can safely cycle to the next frame - // scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) }; scope.cycle_frame(); - true } else { false } } + // The head of the bumpframe is the first linked NodeLink pub fn wip_head(&self, id: &ScopeId) -> &VNode { let scope = self.get_scope(id).unwrap(); let wip_frame = scope.wip_frame(); let nodes = wip_frame.nodes.borrow(); - let node = nodes.get(0).unwrap(); + let node: &VNode = unsafe { &**nodes.get(0).unwrap() }; 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 { let scope = self.get_scope(id).unwrap(); let wip_frame = scope.fin_frame(); let nodes = wip_frame.nodes.borrow(); - let node: &VNode = nodes.get(0).unwrap(); + let node: &VNode = unsafe { &**nodes.get(0).unwrap() }; unsafe { std::mem::transmute::<&VNode, &VNode>(node) } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index c0a85b06..0513b690 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -13,6 +13,9 @@ use std::{any::Any, collections::VecDeque}; /// A virtual node system that progresses user events and diffs UI trees. /// +/// +/// ## Guide +/// /// Components are defined as simple functions that take [`Context`] and a [`Properties`] type and return an [`Element`]. /// /// ```rust @@ -105,14 +108,16 @@ pub struct VirtualDom { _root_caller: *mut dyn Fn(&Scope) -> Element, - pub(crate) scopes: Box, + scopes: Box, receiver: UnboundedReceiver, - pub(crate) sender: UnboundedSender, - // Every component that has futures that need to be polled + sender: UnboundedSender, + pending_futures: FxHashSet, + pending_messages: VecDeque, + dirty_scopes: IndexSet, } @@ -130,7 +135,7 @@ impl VirtualDom { /// /// # Example /// ``` - /// fn Example(cx: Context<()>) -> DomTree { + /// fn Example(cx: Context, props: &()) -> Element { /// cx.render(rsx!( div { "hello world" } )) /// } /// @@ -159,7 +164,7 @@ impl VirtualDom { /// name: &'static str /// } /// - /// fn Example(cx: Context) -> DomTree { + /// fn Example(cx: Context, props: &SomeProps) -> Element { /// cx.render(rsx!{ div{ "hello {cx.name}" } }) /// } /// @@ -172,7 +177,7 @@ impl VirtualDom { /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" }); /// let mutations = dom.rebuild(); /// ``` - pub fn new_with_props(root: FC

, root_props: P) -> Self { + pub fn new_with_props(root: FC

, root_props: P) -> Self { let (sender, receiver) = futures_channel::mpsc::unbounded::(); Self::new_with_props_and_scheduler(root, root_props, sender, receiver) } @@ -230,8 +235,10 @@ impl VirtualDom { /// /// # Example /// + /// ```rust /// - /// + /// + /// ``` pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender { self.sender.clone() } @@ -240,8 +247,10 @@ impl VirtualDom { /// /// # Example /// + /// ```rust /// /// + /// ``` pub fn has_any_work(&self) -> bool { !(self.dirty_scopes.is_empty() && self.pending_messages.is_empty()) } @@ -347,7 +356,9 @@ impl VirtualDom { /// # Example /// /// ```no_run - /// static App: FC<()> = |cx, props|rsx!(cx, div {"hello"} ); + /// fn App(cx: Context, props: &()) -> Element { + /// cx.render(rsx!( div {"hello"} )) + /// } /// /// let mut dom = VirtualDom::new(App); /// @@ -368,6 +379,7 @@ impl VirtualDom { /// applied the edits. /// /// Mutations are the only link between the RealDOM and the VirtualDOM. + /// pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec { let mut committed_mutations = vec![]; @@ -436,10 +448,7 @@ impl VirtualDom { } } - // let scopes = &mut self.scopes; let work_completed = diff_state.work(&mut deadline); - // let work_completed = crate::diff::work(&mut diff_state, &mut deadline); - // let work_completed = crate::diff::work(&mut diff_state, &mut deadline); if work_completed { let DiffState { @@ -454,7 +463,7 @@ impl VirtualDom { } // I think the stack should be empty at the end of diffing? - debug_assert_eq!(stack.scope_stack.len(), 0); + debug_assert_eq!(stack.scope_stack.len(), 1); committed_mutations.push(mutations); } else { @@ -536,74 +545,45 @@ impl VirtualDom { /// let edits = dom.diff(); /// ``` pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option> { - log::debug!("hard diff {:?}", scope_id); - + let mut diff_machine = DiffState::new(&self.scopes); if self.scopes.run_scope(scope_id) { - let mut diff_machine = DiffState::new(&self.scopes); - diff_machine.force_diff = true; - - diff_machine.work(|| false); - // let scopes = &mut self.scopes; - // crate::diff::diff_scope(&mut diff_machine, scope_id); - // self.scopes.diff_scope(&mut diff_machine, scope_id); - - // dbg!(&diff_machine.mutations); - // let DiffState { - // mutations, - // stack, - // seen_scopes, - // force_diff, - // .. - // } = mutations; - - // let mutations = diff_machine.mutations; - - // Some(unsafe { std::mem::transmute(mutations) }) - todo!() - } else { - None + diff_machine.diff_scope(scope_id); } + Some(diff_machine.mutations) } /// Renders an `rsx` call into the Base Scope's allocator. /// /// Useful when needing to render nodes from outside the VirtualDom, such as in a test. pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option>) -> &'a VNode<'a> { - todo!() + 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); + frame.bump.alloc(node) } /// Renders an `rsx` call into the Base Scope's allocator. /// /// Useful when needing to render nodes from outside the VirtualDom, such as in a test. pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> { - todo!() - // let mutations = Mutations::new(); - // let mut machine: DiffState = todo!(); - // // let mut machine = DiffState::new(mutations); - // // let mut machine = DiffState::new(mutations); - // machine.stack.push(DiffInstruction::Diff { new, old }); - // machine.mutations + let mut machine = DiffState::new(&self.scopes); + machine.stack.push(DiffInstruction::Diff { new, old }); + machine.stack.scope_stack.push(self.base_scope); + machine.work(|| false); + machine.mutations } /// Renders an `rsx` call into the Base Scope's allocator. /// /// Useful when needing to render nodes from outside the VirtualDom, such as in a test. pub fn create_vnodes<'a>(&'a self, left: Option>) -> Mutations<'a> { - todo!() - // let old = self.bump.alloc(self.render_direct(left)); - - // let mut machine: DiffState = todo!(); - // // let mut machine = DiffState::new(Mutations::new()); - // // let mut machine = DiffState::new(Mutations::new()); - - // machine.stack.create_node(old, MountType::Append); - - // todo!() - - // // machine.work(&mut || false); - - // // machine.mutations + let nodes = self.render_vnodes(left); + let mut machine = DiffState::new(&self.scopes); + machine.stack.create_node(nodes, MountType::Append); + machine.work(|| false); + machine.mutations } /// Renders an `rsx` call into the Base Scope's allocator. @@ -616,26 +596,17 @@ impl VirtualDom { ) -> (Mutations<'a>, Mutations<'a>) { let (old, new) = (self.render_vnodes(left), self.render_vnodes(right)); - // let mut machine: DiffState = todo!(); - // let mut machine = DiffState::new(Mutations::new()); + let mut create = DiffState::new(&self.scopes); + create.stack.scope_stack.push(self.base_scope); + create.stack.create_node(old, MountType::Append); + create.work(|| false); - // machine.stack.create_node(old, MountType::Append); + let mut edit = DiffState::new(&self.scopes); + create.stack.scope_stack.push(self.base_scope); + edit.stack.push(DiffInstruction::Diff { old, new }); + edit.work(&mut || false); - todo!() - - // machine.work(|| false); - // let create_edits = machine.mutations; - - // let mut machine: DiffState = todo!(); - // // let mut machine = DiffState::new(Mutations::new()); - - // machine.stack.push(DiffInstruction::Diff { old, new }); - - // machine.work(&mut || false); - - // let edits = machine.mutations; - - // (create_edits, edits) + (create.mutations, edit.mutations) } } diff --git a/packages/core/tests/create_dom.rs b/packages/core/tests/create_dom.rs index 00e3debe..fdb9786a 100644 --- a/packages/core/tests/create_dom.rs +++ b/packages/core/tests/create_dom.rs @@ -215,12 +215,12 @@ fn create_components() { }) }; - #[derive(Props)] - struct ChildProps<'a> { - children: ScopeChildren<'a>, + #[derive(Props, PartialEq)] + struct ChildProps { + children: Element, } - fn Child<'a>(cx: Context<'a>, props: &ChildProps<'a>) -> Element { + fn Child(cx: Context, props: &ChildProps) -> Element { cx.render(rsx! { h1 {} div { {&props.children} } @@ -299,21 +299,3 @@ fn anchors() { ] ); } - -#[test] -fn suspended() { - todo!() - // static App: FC<()> = |cx, props| { - // let val = use_suspense(cx, || async {}, |p| todo!()); - - // cx.render(rsx! { {val} }) - // }; - - // let mut dom = new_dom(App, ()); - // let mutations = dom.rebuild(); - - // assert_eq!( - // mutations.edits, - // [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },] - // ); -} diff --git a/packages/core/tests/diffing.rs b/packages/core/tests/diffing.rs index 2e0948e1..871732a0 100644 --- a/packages/core/tests/diffing.rs +++ b/packages/core/tests/diffing.rs @@ -257,12 +257,12 @@ fn many_items_become_fragment() { ] ); - // hmmmmmmmmm worried about reusing IDs that we shouldnt be + // note: the ID gets reused assert_eq!( change.edits, [ Remove { root: 2 }, - CreatePlaceholder { root: 4 }, + CreatePlaceholder { root: 3 }, ReplaceWith { root: 0, m: 1 }, ] ); @@ -433,7 +433,7 @@ fn keyed_diffing_order() { let dom = new_dom(); let left = rsx!( - // {(0..5).map(|f| {rsx! { div { key: "{f}" }}})} + {(0..5).map(|f| {rsx! { div { key: "{f}" }}})} p {"e"} ); let right = rsx!( @@ -802,22 +802,3 @@ fn controlled_keyed_diffing_out_of_order_max_test() { ] ); } - -#[test] -fn suspense() { - let dom = new_dom(); - - todo!() - // let edits = dom.create_vnodes(Some(LazyNodes::new(|f| { - // use std::cell::{Cell, RefCell}; - // VNode::Suspended(f.bump().alloc(VSuspended { - // task_id: 0, - // callback: RefCell::new(None), - // dom_id: Cell::new(None), - // })) - // }))); - // assert_eq!( - // edits.edits, - // [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }] - // ); -} diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 1f5135d8..b6e8ac4c 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -1,7 +1,6 @@ use std::cell::RefCell; use dioxus::prelude::Scope; -use dioxus::ScopeChildren; use dioxus_core as dioxus; use dioxus_core::{Context, Element, LazyNodes, NodeFactory, Properties}; use dioxus_core_macro::Props; @@ -44,7 +43,7 @@ pub struct WebviewWindowProps<'a> { /// focuse me onfocused: &'a dyn FnMut(()), - children: ScopeChildren<'a>, + children: Element, } /// A handle to a @@ -57,7 +56,7 @@ pub struct WebviewWindowProps<'a> { /// /// /// -pub fn WebviewWindow((cx, props): Scope) -> Element { +pub fn WebviewWindow(cx: Context, props: &WebviewWindowProps) -> Element { let dtcx = cx.consume_state::>()?; cx.use_hook( @@ -91,7 +90,7 @@ fn syntax_works() { use dioxus_hooks::*; use dioxus_html as dioxus_elements; - static App: FC<()> = |(cx, props)| { + static App: FC<()> = |cx, props| { cx.render(rsx! { // left window WebviewWindow { diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index a7160fa8..495deebd 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -166,7 +166,7 @@ pub fn run( sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap(); if let Some(BridgeEvent::Update(edits)) = rx.blocking_recv() { - log::info!("bridge received message {:?}", edits); + log::info!("bridge received message"); Some(RpcResponse::new_result(req.id.take(), Some(edits))) } else { log::info!("none received message"); @@ -276,7 +276,7 @@ pub(crate) fn launch_vdom_with_tokio( runtime.block_on(async move { let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver); - let _ = vir.get_event_sender(); + let _ = vir.get_scheduler_channel(); let edits = vir.rebuild(); @@ -298,14 +298,13 @@ pub(crate) fn launch_vdom_with_tokio( // todo: maybe we want to schedule ourselves in // on average though, the virtualdom running natively is stupid fast - let mut muts = vir.run_with_deadline(|| false); + let mut muts = vir.work_with_deadline(|| false); log::debug!("finished running with deadline"); let mut edits = vec![]; while let Some(edit) = muts.pop() { - log::debug!("sending message on channel with edit {:?}", edit); let edit_string = serde_json::to_value(Evt { edits: edit.edits }) .expect("serializing edits should never fail"); edits.push(edit_string); diff --git a/packages/ssr/index.html b/packages/ssr/index.html new file mode 100644 index 00000000..10500012 --- /dev/null +++ b/packages/ssr/index.html @@ -0,0 +1 @@ +asd \ No newline at end of file diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index 9e5faa69..4fc696c1 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -14,7 +14,7 @@ use std::fmt::{Display, Formatter}; use dioxus_core::exports::bumpalo; use dioxus_core::exports::bumpalo::Bump; -use dioxus_core::nodes::IntoVNode; +use dioxus_core::IntoVNode; use dioxus_core::*; /// A memory pool for rendering @@ -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,6 +158,9 @@ impl<'a> TextRenderer<'a, '_> { } write!(f, "")?; } + VNode::Linked(link) => { + todo!(); + } VNode::Element(el) => { if self.cfg.indent { for _ in 0..il { @@ -244,7 +247,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 { } @@ -297,18 +300,17 @@ impl SsrConfig { mod tests { use super::*; - use dioxus_core as dioxus; use dioxus_core::prelude::*; use dioxus_core_macro::*; use dioxus_html as dioxus_elements; - static SIMPLE_APP: FC<()> = |(cx, _)| { + static SIMPLE_APP: FC<()> = |cx, _| { cx.render(rsx!(div { "hello world!" })) }; - static SLIGHTLY_MORE_COMPLEX: FC<()> = |(cx, _)| { + static SLIGHTLY_MORE_COMPLEX: FC<()> = |cx, _| { cx.render(rsx! { div { title: "About W3Schools" @@ -327,14 +329,14 @@ mod tests { }) }; - static NESTED_APP: FC<()> = |(cx, _)| { + static NESTED_APP: FC<()> = |cx, _| { cx.render(rsx!( div { SIMPLE_APP {} } )) }; - static FRAGMENT_APP: FC<()> = |(cx, _)| { + static FRAGMENT_APP: FC<()> = |cx, _| { cx.render(rsx!( div { "f1" } div { "f2" } @@ -390,7 +392,7 @@ mod tests { #[test] fn styles() { - static STLYE_APP: FC<()> = |(cx, _)| { + static STLYE_APP: FC<()> = |cx, _| { cx.render(rsx! { div { color: "blue", font_size: "46px" } }) diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 2e9704b9..2d109f02 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -7,17 +7,12 @@ //! - tests to ensure dyn_into works for various event types. //! - Partial delegation?> -use dioxus_core::{ - events::{KeyCode, UserEvent}, - mutations::NodeRefMutation, - scheduler::SchedulerMsg, - DomEdit, ElementId, ScopeId, -}; +use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent}; use fxhash::FxHashMap; -use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc}; +use std::{any::Any, fmt::Debug, rc::Rc}; use wasm_bindgen::{closure::Closure, JsCast}; use web_sys::{ - Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement, + CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, HtmlTextAreaElement, Node, NodeList, }; @@ -55,7 +50,7 @@ impl WebsysDom { let document = load_document(); let mut nodes = NodeSlab::new(2000); - let mut listeners = FxHashMap::default(); + let listeners = FxHashMap::default(); // re-hydrate the page - only supports one virtualdom per page if cfg.hydrate { @@ -90,17 +85,17 @@ impl WebsysDom { } } - pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) { - for item in refs { - if let Some(bla) = &item.element { - let node = self.nodes[item.element_id.as_u64() as usize] - .as_ref() - .unwrap() - .clone(); - bla.set(Box::new(node)).unwrap(); - } - } - } + // pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) { + // for item in refs { + // if let Some(bla) = &item.element { + // let node = self.nodes[item.element_id.as_u64() as usize] + // .as_ref() + // .unwrap() + // .clone(); + // bla.set(Box::new(node)).unwrap(); + // } + // } + // } pub fn process_edits(&mut self, edits: &mut Vec) { for edit in edits.drain(..) { @@ -309,8 +304,8 @@ impl WebsysDom { } } - fn remove_event_listener(&mut self, event: &str, root: u64) { - // todo!() + fn remove_event_listener(&mut self, _event: &str, _root: u64) { + todo!() } fn set_text(&mut self, text: &str, root: u64) { @@ -489,8 +484,9 @@ unsafe impl Sync for DioxusWebsysEvent {} // todo: some of these events are being casted to the wrong event type. // We need tests that simulate clicks/etc and make sure every event type works. fn virtual_event_from_websys_event(event: web_sys::Event) -> Box { - use crate::events::*; - use dioxus_core::events::on::*; + use dioxus_html::on::*; + use dioxus_html::KeyCode; + // use dioxus_core::events::on::*; match event.type_().as_str() { "copy" | "cut" | "paste" => Box::new(ClipboardEvent {}), "compositionend" | "compositionstart" | "compositionupdate" => { @@ -682,15 +678,6 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { let typ = event.type_(); - // TODO: clean this up - if cfg!(debug_assertions) { - let attrs = target.attributes(); - for x in 0..attrs.length() { - let attr: Attr = attrs.item(x).unwrap(); - // log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value()); - } - } - use anyhow::Context; // The error handling here is not very descriptive and needs to be replaced with a zero-cost error system @@ -716,7 +703,8 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { name: event_name_from_typ(&typ), event: virtual_event_from_websys_event(event.clone()), mounted_dom_id: Some(ElementId(real_id as usize)), - scope: ScopeId(triggered_scope as usize), + scope_id: ScopeId(triggered_scope as usize), + priority: dioxus_core::EventPriority::Medium, }) } diff --git a/packages/web/src/events.rs b/packages/web/src/events.rs deleted file mode 100644 index 01c32185..00000000 --- a/packages/web/src/events.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Ported events into Dioxus Synthetic Event system - -use dioxus_core::events::on::*; -use wasm_bindgen::JsCast; -use web_sys::{Event, UiEvent}; diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 5b84add8..4e5f5fef 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -57,8 +57,8 @@ use std::rc::Rc; pub use crate::cfg::WebConfig; use crate::dom::load_document; use cache::intern_cached_strings; -use dioxus::prelude::Properties; -use dioxus::virtual_dom::VirtualDom; +use dioxus::SchedulerMsg; +use dioxus::VirtualDom; pub use dioxus_core as dioxus; use dioxus_core::prelude::FC; use futures_util::FutureExt; @@ -66,7 +66,6 @@ use futures_util::FutureExt; mod cache; mod cfg; mod dom; -mod events; mod nodeslab; mod ric_raf; @@ -144,9 +143,10 @@ pub async fn run_with_props(root: FC, root_props: T, cfg: let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap(); - let tasks = dom.get_event_sender(); + let tasks = dom.get_scheduler_channel(); - let sender_callback = Rc::new(move |event| tasks.unbounded_send(event).unwrap()); + let sender_callback: Rc = + Rc::new(move |event| tasks.unbounded_send(event).unwrap()); let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback); @@ -170,13 +170,12 @@ pub async fn run_with_props(root: FC, root_props: T, cfg: let mut deadline = work_loop.wait_for_idle_time().await; // run the virtualdom work phase until the frame deadline is reached - let mutations = dom.run_with_deadline(|| (&mut deadline).now_or_never().is_some()); + let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some()); // wait for the animation frame to fire so we can apply our changes work_loop.wait_for_raf().await; for mut edit in mutations { - // log::debug!("edits are {:#?}", edit); // actually apply our changes during the animation frame websys_dom.process_edits(&mut edit.edits); }