diff --git a/.vscode/settings.json b/.vscode/settings.json index b3bde867..ac2195cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,7 @@ { - "rust-analyzer.cargo.allFeatures": true + "rust-analyzer.cargo.allFeatures": true, + "rust-analyzer.cargo.features": [ + "desktop", + "router" + ], } diff --git a/README.md b/README.md index a5f0e844..fddea288 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust. ```rust -fn App(cx: Scope<()>) -> Element { +fn app(cx: Scope<()>) -> Element { let mut count = use_state(&cx, || 0); cx.render(rsx!( @@ -76,23 +76,21 @@ If you know React, then you already know Dioxus. - - - - - - + + + + + +
WebDesktopMobileStateDocsToolsWebDesktopMobileStateDocsTools
## Examples: -| File Navigator (Desktop) | Bluetooth scanner (Desktop) | TodoMVC (All platforms) | Widget Gallery | +| File Navigator (Desktop) | Bluetooth scanner (Desktop) | TodoMVC (All platforms) | Tailwind (Liveview) | | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | [![asd](https://github.com/DioxusLabs/file-explorer-example/raw/master/image.png)](https://github.com/DioxusLabs/file-explorer-example) | [![asd](https://github.com/DioxusLabs/file-explorer-example/raw/master/image.png)](https://github.com/DioxusLabs/file-explorer-example) | [![asd](https://github.com/DioxusLabs/todomvc/raw/master/example.png)](https://github.com/dioxusLabs/todomvc/) | [![asd](https://github.com/DioxusLabs/file-explorer-example/raw/master/image.png)](https://github.com/DioxusLabs/file-explorer-example) | - - See the awesome-dioxus page for a curated list of content in the Dioxus Ecosystem. @@ -169,6 +167,7 @@ Dioxus is heavily inspired by React, but we want your transition to feel like an | 1st class global state | ✅ | ✅ | redux/recoil/mobx on top of context | | Runs natively | ✅ | ❓ | runs as a portable binary w/o a runtime (Node) | | Subtree Memoization | ✅ | ❓ | skip diffing static element subtrees | +| High-efficiency templates | 🛠 | ❓ | rsx! calls are translated to templates on the DOM's side | | Compile-time correct | ✅ | ❓ | Throw errors on invalid template layouts | | Heuristic Engine | ✅ | ❓ | track component memory usage to minimize future allocations | | Fine-grained reactivity | 👀 | ❓ | Skip diffing for fine-grain updates | diff --git a/docs/guide/src/README.md b/docs/guide/src/README.md index faf58e30..41982862 100644 --- a/docs/guide/src/README.md +++ b/docs/guide/src/README.md @@ -6,7 +6,7 @@ ```rust fn App(cx: Context, props: &()) -> Element { - let mut count = use_state(cx, || 0); + let mut count = use_state(&cx, || 0); cx.render(rsx!( h1 { "High-Five counter: {count}" } @@ -53,7 +53,7 @@ Dioxus supports server-side rendering! For rendering statically to an `.html` file or from a WebServer, then you'll want to make sure the `ssr` feature is enabled in the `dioxus` crate and use the `dioxus::ssr` API. We don't expect the SSR API to change drastically in the future. ```rust -let contents = dioxus::ssr::render_vdom(&dom, |c| c); +let contents = dioxus::ssr::render_vdom(&dom); ``` [Jump to the getting started guide for SSR.]() diff --git a/docs/guide/src/concepts/00-index.md b/docs/guide/src/concepts/00-index.md index 715c3d69..cc432cf9 100644 --- a/docs/guide/src/concepts/00-index.md +++ b/docs/guide/src/concepts/00-index.md @@ -45,7 +45,7 @@ As the UI grows in scale, our logic to keep each element in the proper state wou Instead, with Dioxus, we *declare* what we want our UI to look like: ```rust -let mut state = use_state(cx, || "red"); +let mut state = use_state(&cx, || "red"); cx.render(rsx!( Container { diff --git a/docs/guide/src/concepts/11-arena-memo.md b/docs/guide/src/concepts/11-arena-memo.md index 28eccda2..12f99a1d 100644 --- a/docs/guide/src/concepts/11-arena-memo.md +++ b/docs/guide/src/concepts/11-arena-memo.md @@ -21,9 +21,9 @@ fn test() -> DomTree { } } -static TestComponent: FC<()> = |cx, props|html!{
"Hello world"
}; +static TestComponent: Component<()> = |cx, props|html!{
"Hello world"
}; -static TestComponent: FC<()> = |cx, props|{ +static TestComponent: Component<()> = |cx, props|{ let g = "BLAH"; html! {
"Hello world"
@@ -31,7 +31,7 @@ static TestComponent: FC<()> = |cx, props|{ }; #[functional_component] -static TestComponent: FC<{ name: String }> = |cx, props|html! {
"Hello {name}"
}; +static TestComponent: Component<{ name: String }> = |cx, props|html! {
"Hello {name}"
}; ``` ## Why this behavior? diff --git a/docs/guide/src/concepts/12-signals.md b/docs/guide/src/concepts/12-signals.md index 5000faeb..3737a488 100644 --- a/docs/guide/src/concepts/12-signals.md +++ b/docs/guide/src/concepts/12-signals.md @@ -12,7 +12,7 @@ Your component today might look something like this: ```rust fn Comp(cx: Context<()>) -> DomTree { - let (title, set_title) = use_state(cx, || "Title".to_string()); + let (title, set_title) = use_state(&cx, || "Title".to_string()); cx.render(rsx!{ input { value: title, @@ -26,7 +26,7 @@ This component is fairly straightforward - the input updates its own value on ev ```rust fn Comp(cx: Context<()>) -> DomTree { - let (title, set_title) = use_state(cx, || "Title".to_string()); + let (title, set_title) = use_state(&cx, || "Title".to_string()); cx.render(rsx!{ div { input { @@ -96,7 +96,7 @@ Sometimes you want a signal to propagate across your app, either through far-awa ```rust const TITLE: Atom = || "".to_string(); -const Provider: FC<()> = |cx, props|{ +const Provider: Component<()> = |cx, props|{ let title = use_signal(&cx, &TITLE); rsx!(cx, input { value: title }) }; @@ -105,7 +105,7 @@ const Provider: FC<()> = |cx, props|{ If we use the `TITLE` atom in another component, we can cause updates to flow between components without calling render or diffing either component trees: ```rust -const Receiver: FC<()> = |cx, props|{ +const Receiver: Component<()> = |cx, props|{ let title = use_signal(&cx, &TITLE); log::info!("This will only be called once!"); rsx!(cx, @@ -132,7 +132,7 @@ Dioxus automatically understands how to use your signals when mixed with iterato ```rust const DICT: AtomFamily = |_| {}; -const List: FC<()> = |cx, props|{ +const List: Component<()> = |cx, props|{ let dict = use_signal(&cx, &DICT); cx.render(rsx!( ul { diff --git a/docs/guide/src/concepts/components.md b/docs/guide/src/concepts/components.md index ef750f87..9eb67ab3 100644 --- a/docs/guide/src/concepts/components.md +++ b/docs/guide/src/concepts/components.md @@ -124,7 +124,7 @@ fn VoteButton(cx: Context, props: &VoteButtonProps) -> Element { cx.render(rsx!{ div { class: "votebutton" div { class: "arrow up" } - div { class: "score", "{props.score}"} + div { class: "score", "{cx.props.score}"} div { class: "arrow down" } } }) @@ -147,7 +147,7 @@ struct TitleCardProps<'a> { fn TitleCard(cx: Context, props: &TitleCardProps) -> Element { cx.render(rsx!{ - h1 { "{props.title}" } + h1 { "{cx.props.title}" } }) } ``` diff --git a/docs/guide/src/concepts/conditional_rendering.md b/docs/guide/src/concepts/conditional_rendering.md index e35fce36..ef62b870 100644 --- a/docs/guide/src/concepts/conditional_rendering.md +++ b/docs/guide/src/concepts/conditional_rendering.md @@ -83,7 +83,7 @@ fn App(cx: Context, props: &())-> Element { This syntax even enables us to write a one-line component: ```rust -static App: FC<()> = |cx, props| rsx!(cx, "hello world!"); +static App: Component<()> = |cx, props| rsx!(cx, "hello world!"); ``` Alternatively, for match statements, we can just return the builder itself and pass it into a final, single call to `cx.render`: diff --git a/docs/guide/src/concepts/exporting_components.md b/docs/guide/src/concepts/exporting_components.md index 9b5a65ca..6139a97e 100644 --- a/docs/guide/src/concepts/exporting_components.md +++ b/docs/guide/src/concepts/exporting_components.md @@ -22,7 +22,7 @@ Let's say our app looks something like this: use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } fn App((cx, props): Component<()>) -> Element {} @@ -87,7 +87,7 @@ In our `main.rs`, we'll want to declare the `post` module so we can access our ` use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } mod post; @@ -186,7 +186,7 @@ Ultimately, including and exporting components is governed by Rust's module syst use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } mod post; diff --git a/docs/guide/src/concepts/interactivity.md b/docs/guide/src/concepts/interactivity.md index bd8ff14a..63cf6741 100644 --- a/docs/guide/src/concepts/interactivity.md +++ b/docs/guide/src/concepts/interactivity.md @@ -64,7 +64,7 @@ The most common hook you'll use for storing state is `use_state`. `use_state` pr ```rust fn App(cx: Context, props: &())-> Element { - let post = use_state(cx, || { + let post = use_state(&cx, || { PostData { id: Uuid::new_v4(), score: 10, @@ -112,7 +112,7 @@ For example, let's say we provide a button to generate a new post. Whenever the ```rust fn App(cx: Context, props: &())-> Element { - let post = use_state(cx, || PostData::new()); + let post = use_state(&cx, || PostData::new()); cx.render(rsx!{ button { @@ -135,7 +135,7 @@ We can use tasks in our components to build a tiny stopwatch that ticks every se ```rust fn App(cx: Context, props: &())-> Element { - let mut sec_elapsed = use_state(cx, || 0); + let mut sec_elapsed = use_state(&cx, || 0); cx.spawn_task(async move { TimeoutFuture::from_ms(1000).await; diff --git a/docs/guide/src/hello_world.md b/docs/guide/src/hello_world.md index 2520c286..2b0cebf8 100644 --- a/docs/guide/src/hello_world.md +++ b/docs/guide/src/hello_world.md @@ -92,7 +92,7 @@ use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } fn App(cx: Context, props: &()) -> Element { @@ -118,7 +118,7 @@ This initialization code launches a Tokio runtime on a helper thread where your ```rust fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } ``` @@ -135,7 +135,7 @@ fn App(cx: Context, props: &()) -> Element { Writing `fn App(cx: Context, props: &()) -> Element {` might become tedious. Rust will also let you write functions as static closures, but these types of Components cannot have props that borrow data. ```rust -static App: FC<()> = |cx, props| cx.render(rsx!(div { "Hello, world!" })); +static App: Component<()> = |cx, props| cx.render(rsx!(div { "Hello, world!" })); ``` ### What is this `Context` object? diff --git a/examples/README.md b/examples/README.md index 06a73db0..2c05466a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -72,7 +72,7 @@ fn App(cx: Context, props: &()) -> Element { struct ToggleProps { children: Element } fn Toggle(cx: Context, props: &ToggleProps) -> Element { - let mut toggled = use_state(cx, || false); + let mut toggled = use_state(&cx, || false); cx.render(rsx!{ div { {&props.children} @@ -87,7 +87,7 @@ fn Toggle(cx: Context, props: &ToggleProps) -> Element { Controlled inputs: ```rust fn App(cx: Context, props: &()) -> Element { - let value = use_state(cx, String::new); + let value = use_state(&cx, String::new); cx.render(rsx!( input { "type": "text", @@ -123,13 +123,13 @@ fn App(cx: Context, props: &()) -> Element { Tiny components: ```rust -static App: FC<()> = |cx, _| rsx!(cx, div {"hello world!"}); +static App: Component<()> = |cx, _| rsx!(cx, div {"hello world!"}); ``` Borrowed prop contents: ```rust fn App(cx: Context, props: &()) -> Element { - let name = use_state(cx, || String::from("example")); + let name = use_state(&cx, || String::from("example")); rsx!(cx, Child { title: name.as_str() }) } @@ -137,7 +137,7 @@ fn App(cx: Context, props: &()) -> Element { struct ChildProps<'a> { title: &'a str } fn Child(cx: Context, props: &ChildProps) -> Element { - rsx!(cx, "Hello {props.title}") + rsx!(cx, "Hello {cx.props.title}") } ``` diff --git a/examples/async.rs b/examples/async.rs index 7143a9af..16f28809 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -8,12 +8,12 @@ use gloo_timers::future::TimeoutFuture; #[tokio::main] async fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } -pub static App: Component<()> = |cx, _| { - let count = use_state(cx, || 0); - let mut direction = use_state(cx, || 1); +pub static App: Component<()> = |cx| { + let count = use_state(&cx, || 0); + let mut direction = use_state(&cx, || 1); let (async_count, dir) = (count.for_async(), *direction); diff --git a/examples/borrowed.rs b/examples/borrowed.rs index 35ec2cfb..cf497bbc 100644 --- a/examples/borrowed.rs +++ b/examples/borrowed.rs @@ -17,10 +17,10 @@ and is proven to be safe with MIRI. use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } -fn App(cx: Scope, props: &()) -> Element { +fn App(cx: Scope<()>) -> Element { let text: &mut Vec = cx.use_hook(|_| vec![String::from("abc=def")], |f| f); let first = text.get_mut(0).unwrap(); @@ -39,7 +39,7 @@ struct C1Props<'a> { text: &'a mut String, } -fn Child1(cx: Scope, props: &C1Props) -> Element { +fn Child1<'a>(cx: Scope<'a, C1Props<'a>>) -> Element { let (left, right) = props.text.split_once("=").unwrap(); cx.render(rsx! { @@ -55,7 +55,7 @@ struct C2Props<'a> { text: &'a str, } -fn Child2(cx: Scope, props: &C2Props) -> Element { +fn Child2<'a>(cx: Scope<'a, C2Props<'a>>) -> Element { cx.render(rsx! { Child3 { text: props.text @@ -68,8 +68,8 @@ struct C3Props<'a> { text: &'a str, } -fn Child3(cx: Scope, props: &C3Props) -> Element { +fn Child3<'a>(cx: Scope<'a, C3Props<'a>>) -> Element { cx.render(rsx! { - div { "{props.text}"} + div { "{cx.props.text}"} }) } diff --git a/examples/calculator.rs b/examples/calculator.rs index 442ddb7f..6a83bfa6 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -10,13 +10,13 @@ use dioxus::prelude::*; use separator::Separatable; fn main() { - dioxus::desktop::launch(APP, |cfg| cfg); + dioxus::desktop::launch(APP); } -const APP: Component<()> = |cx, _| { - let cur_val = use_state(cx, || 0.0_f64); - let operator = use_state(cx, || None as Option<&'static str>); - let display_value = use_state(cx, || String::from("")); +static APP: Component<()> = |cx| { + let cur_val = use_state(&cx, || 0.0_f64); + let operator = use_state(&cx, || None as Option<&'static str>); + let display_value = use_state(&cx, || String::from("")); let toggle_percent = move |_| todo!(); let input_digit = move |num: u8| display_value.modify().push_str(num.to_string().as_str()); @@ -117,13 +117,13 @@ const APP: Component<()> = |cx, _| { struct CalculatorKeyProps<'a> { name: &'static str, onclick: &'a dyn Fn(Arc), - children: Element, + children: Element<'a>, } -fn CalculatorKey<'a>(cx: Scope, props: &CalculatorKeyProps) -> Element { +fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element { rsx!(cx, button { - class: "calculator-key {props.name}" - onclick: {props.onclick} + class: "calculator-key {cx.props.name}" + onclick: {cx.props.onclick} {&props.children} }) } diff --git a/examples/core/alternative.rs b/examples/core/alternative.rs index 6c20e2ad..a009a88c 100644 --- a/examples/core/alternative.rs +++ b/examples/core/alternative.rs @@ -9,7 +9,7 @@ fn main() { println!("{}", dom); } -pub static EXAMPLE: FC<()> = |cx, _| { +pub static EXAMPLE: Component<()> = |cx| { let list = (0..10).map(|_f| { rsx! { "{_f}" diff --git a/examples/core/async.rs b/examples/core/async.rs index a21f8265..2919651d 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: Component<()> = |cx| { 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 6c90a60c..d538d49a 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: Component<()> = |cx| { todo!() // let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap()); diff --git a/examples/core/jsframework.rs b/examples/core/jsframework.rs index 207f6e84..15c25ec8 100644 --- a/examples/core/jsframework.rs +++ b/examples/core/jsframework.rs @@ -47,9 +47,9 @@ fn Row<'a>((cx, props): Scope<'a, RowProps>) -> Element<'a> { }; cx.render(rsx! { tr { - // td { class:"col-md-1", "{props.row_id}" } + // td { class:"col-md-1", "{cx.props.row_id}" } // td { class:"col-md-1", onclick: move |_| { /* run onselect */ } - // a { class: "lbl", "{props.label}" } + // a { class: "lbl", "{cx.props.label}" } // } // td { class: "col-md-1" // a { class: "remove", onclick: {handler} diff --git a/examples/core/syntax.rs b/examples/core/syntax.rs index 9a21bc7e..2339af8a 100644 --- a/examples/core/syntax.rs +++ b/examples/core/syntax.rs @@ -31,9 +31,9 @@ fn html_usage() { // let p = rsx!(div { {f} }); } -static App2: FC<()> = |cx, _| cx.render(rsx!("hello world!")); +static App2: Component<()> = |cx, _| cx.render(rsx!("hello world!")); -static App: FC<()> = |cx, props| { +static App: Component<()> = |cx| { let name = cx.use_state(|| 0); cx.render(rsx!(div { @@ -71,7 +71,7 @@ struct ChildProps { fn Child<'a>((cx, props): Scope<'a, ChildProps>) -> Element<'a> { cx.render(rsx!(div { - // {props.children} + // {cx.props.children} })) } @@ -99,7 +99,7 @@ impl<'a> Children<'a> { } } -static Bapp: FC<()> = |cx, props| { +static Bapp: Component<()> = |cx| { 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: Component<()> = |cx| { // let b: Box VNode> = Box::new(|f| todo!()); diff --git a/examples/core/syntax2.rs b/examples/core/syntax2.rs index 54c0beda..0b3a9e04 100644 --- a/examples/core/syntax2.rs +++ b/examples/core/syntax2.rs @@ -40,7 +40,7 @@ fn t() { // let a = rsx! { // div { // "asd" -// "{props.foo}" +// "{cx.props.foo}" // } // }; diff --git a/examples/core/vdom_usage.rs b/examples/core/vdom_usage.rs index cbe3c324..84417406 100644 --- a/examples/core/vdom_usage.rs +++ b/examples/core/vdom_usage.rs @@ -4,7 +4,7 @@ use dioxus_core::{lazynodes::LazyNodes, prelude::*}; // #[async_std::main] fn main() { - static App: FC<()> = + static App: Component<()> = |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 735a5710..50fecc4f 100644 --- a/examples/core_reference/antipatterns.rs +++ b/examples/core_reference/antipatterns.rs @@ -32,14 +32,14 @@ use dioxus::prelude::*; struct NoKeysProps { data: std::collections::HashMap, } -static AntipatternNoKeys: FC = |cx, props| { +static AntipatternNoKeys: Component = |cx| { // WRONG: Make sure to add keys! rsx!(cx, ul { - {props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))} + {cx.props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))} }); // RIGHT: Like this: rsx!(cx, ul { - {props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))} + {cx.props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "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: Component<()> = |cx| { // Try to avoid heavily nesting fragments rsx!(cx, Fragment { @@ -82,8 +82,8 @@ 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| { - let (state, set_state) = use_state(cx, || "Hello world").classic(); +static AntipatternRelyingOnSetState: Component<()> = |cx| { + let (state, set_state) = use_state(&cx, || "Hello world").classic(); set_state("New state"); // This will return false! `state` will *still* be "Hello world" assert!(state == &"New state"); @@ -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: Component<()> = |cx, props| todo!(); /// Antipattern: Misusing hooks /// --------------------------- @@ -120,11 +120,11 @@ static antipattern_component: FC<()> = |cx, props| todo!(); struct MisuedHooksProps { should_render_state: bool, } -static AntipatternMisusedHooks: FC = |cx, props| { +static AntipatternMisusedHooks: Component = |cx| { if props.should_render_state { // do not place a hook in the conditional! // prefer to move it out of the conditional - let (state, set_state) = use_state(cx, || "hello world").classic(); + let (state, set_state) = use_state(&cx, || "hello world").classic(); rsx!(cx, div { "{state}" }) } else { rsx!(cx, div { "Not rendering state" }) @@ -153,7 +153,7 @@ static AntipatternMisusedHooks: FC = |cx, props| { /// } /// } /// }) -static _example: FC<()> = |cx, props| todo!(); +static _example: Component<()> = |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: Component<()> = |cx, props| todo!(); -pub static Example: FC<()> = |cx, props| { +pub static Example: Component<()> = |cx| { 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 6a3807bb..11c3a59c 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: Component<()> = |cx| { cx.render(rsx! { div { Greeting { @@ -25,10 +25,10 @@ struct GreetingProps { name: &'static str, } -static Greeting: FC = |cx, props| { +static Greeting: Component = |cx| { cx.render(rsx! { div { - h1 { "Hello, {props.name}!" } + h1 { "Hello, {cx.props.name}!" } p { "Welcome to the Dioxus framework" } br {} {cx.children()} diff --git a/examples/core_reference/children.rs b/examples/core_reference/children.rs index bdd3108d..48c940b6 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: Component<()> = |cx| { cx.render(rsx! { div { Banner { @@ -31,7 +31,7 @@ pub static Example: FC<()> = |cx, props| { }) }; -pub static Banner: FC<()> = |cx, props| { +pub static Banner: Component<()> = |cx| { 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 6a5fe9a6..d73f562e 100644 --- a/examples/core_reference/conditional_rendering.rs +++ b/examples/core_reference/conditional_rendering.rs @@ -16,10 +16,10 @@ use dioxus::prelude::*; pub struct MyProps { should_show: bool, } -pub static Example0: FC = |cx, props| { +pub static Example0: Component = |cx| { cx.render(rsx! { div { - {props.should_show.then(|| rsx!{ + {cx.props.should_show.then(|| rsx!{ h1 { "showing the title!" } })} } @@ -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: Component = |cx| { 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: Component = |cx| { cx.render(rsx! { div { {match props.color { @@ -89,9 +89,9 @@ pub static Example2: FC = |cx, props| { }) }; -pub static Example: FC<()> = |cx, props| { - let should_show = use_state(cx, || false); - let mut color_index = use_state(cx, || 0); +pub static Example: Component<()> = |cx| { + let should_show = use_state(&cx, || false); + let mut color_index = use_state(&cx, || 0); let color = match *color_index % 2 { 2 => Color::Green, 1 => Color::Yellow, diff --git a/examples/core_reference/controlled_inputs.rs b/examples/core_reference/controlled_inputs.rs index bdabe83d..2c97ec1f 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: Component<()> = |cx| { cx.render(rsx! { div { @@ -10,8 +10,8 @@ pub static Example: FC<()> = |cx, props| { }; // A controlled component: -static ControlledSelect: FC<()> = |cx, props| { - let value = use_state(cx, || String::from("Grapefruit")); +static ControlledSelect: Component<()> = |cx| { + let value = use_state(&cx, || String::from("Grapefruit")); cx.render(rsx! { select { value: "{value}", onchange: move |evt| value.set(evt.value()), option { value: "Grapefruit", "Grapefruit"} @@ -23,8 +23,8 @@ static ControlledSelect: FC<()> = |cx, props| { }; // TODO - how do uncontrolled things work? -static UncontrolledSelect: FC<()> = |cx, props| { - let value = use_state(cx, || String::new()); +static UncontrolledSelect: Component<()> = |cx| { + let value = use_state(&cx, || String::new()); cx.render(rsx! { select { diff --git a/examples/core_reference/custom_elements.rs b/examples/core_reference/custom_elements.rs index 053b3aea..63024213 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: Component<()> = |cx| { cx.render(rsx! { div { custom_element { diff --git a/examples/core_reference/empty.rs b/examples/core_reference/empty.rs index 28914bd6..aebf7e71 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: Component<()> = |cx, props| cx.render(rsx! { Fragment {} }); diff --git a/examples/core_reference/errorhandling.rs b/examples/core_reference/errorhandling.rs index e598726e..cc21a62f 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: Component<()> = |cx| { 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: Component<()> = |cx| { 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: Component<()> = |cx| { 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: Component<()> = |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: Component<()> = |cx| { 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: Component<()> = |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 88c9dcae..0532abd2 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: Component<()> = |cx| { 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: Component<()> = |cx| { 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: Component<()> = |cx| { 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: Component<()> = |cx| { cx.render(rsx! { App1 {} App2 {} diff --git a/examples/core_reference/global_css.rs b/examples/core_reference/global_css.rs index 5e7c75a8..6b7de70f 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: Component<()> = |cx| { cx.render(rsx! { head { style { "{STYLE}" } } body { diff --git a/examples/core_reference/inline_styles.rs b/examples/core_reference/inline_styles.rs index e3a45154..c60364b7 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: Component<()> = |cx| { 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: Component<()> = |cx| { cx.render(rsx! { div { color: "red" "hello world!" diff --git a/examples/core_reference/iterators.rs b/examples/core_reference/iterators.rs index f8feae8a..55d725a2 100644 --- a/examples/core_reference/iterators.rs +++ b/examples/core_reference/iterators.rs @@ -12,8 +12,8 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |cx, props| { - let example_data = use_state(cx, || 0); +pub static Example: Component<()> = |cx| { + let example_data = use_state(&cx, || 0); let v = (0..10).map(|f| { rsx! { diff --git a/examples/core_reference/listener.rs b/examples/core_reference/listener.rs index 63813e42..891ee374 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: Component<()> = |cx| { cx.render(rsx! { ButtonList {} NonUpdatingEvents {} @@ -16,8 +16,8 @@ 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| { - let name = use_state(cx, || "...?"); +static ButtonList: Component<()> = |cx| { + let name = use_state(&cx, || "...?"); let names = ["jack", "jill", "john", "jane"] .iter() @@ -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: Component<()> = |cx| { 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: Component<()> = |cx| { 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 40aa5419..86831842 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: Component<()> = |cx| { cx.render(rsx! { div { "100% memoized!" } }) @@ -35,9 +35,9 @@ pub struct MyProps1 { name: String, } -pub static Example1: FC = |cx, props| { +pub static Example1: Component = |cx| { cx.render(rsx! { - div { "100% memoized! {props.name}" } + div { "100% memoized! {cx.props.name}" } }) }; @@ -49,9 +49,9 @@ pub struct MyProps2 { name: std::rc::Rc, } -pub static Example2: FC = |cx, props| { +pub static Example2: Component = |cx| { cx.render(rsx! { - div { "100% memoized! {props.name}" } + div { "100% memoized! {cx.props.name}" } }) }; @@ -61,11 +61,11 @@ pub struct MyProps3<'a> { name: &'a str, } // We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime. -// Using the `pub static Example: FC<()>` pattern _will_ specify a lifetime, but that lifetime will be static which might +// Using the `pub static Example: Component<()>` pattern _will_ specify a lifetime, but that lifetime will be static which might // not exactly be what you want fn Example3<'a>(cx: Context<'a>, props: &'a MyProps3) -> DomTree<'a> { cx.render(rsx! { - div { "Not memoized! {props.name}" } + div { "Not memoized! {cx.props.name}" } }) } diff --git a/examples/core_reference/noderefs.rs b/examples/core_reference/noderefs.rs index 8d22ee5b..c520abdc 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: Component<()> = |cx| { let p = 10; cx.render(rsx! { diff --git a/examples/core_reference/signals.rs b/examples/core_reference/signals.rs index 2e69d8e6..06fb6ab1 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: Component<()> = |cx| { cx.render(rsx! { div { diff --git a/examples/core_reference/spreadpattern.rs b/examples/core_reference/spreadpattern.rs index 58a813cb..c197f87e 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: Component<()> = |cx| { let props = MyProps { count: 0, live: true, @@ -27,7 +27,7 @@ pub struct MyProps { name: &'static str, } -pub static Example1: FC = |cx, MyProps { count, live, name }| { +pub static Example1: Component = |cx, MyProps { count, live, name }| { cx.render(rsx! { div { h1 { "Hello, {name}"} diff --git a/examples/core_reference/statemanagement.rs b/examples/core_reference/statemanagement.rs index 2e69d8e6..06fb6ab1 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: Component<()> = |cx| { cx.render(rsx! { div { diff --git a/examples/core_reference/suspense.rs b/examples/core_reference/suspense.rs index e3bbd257..bbe536d3 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: Component<()> = |cx| { 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 a5e877bc..7fad9438 100644 --- a/examples/core_reference/task.rs +++ b/examples/core_reference/task.rs @@ -24,9 +24,9 @@ use dioxus::prelude::*; -pub static Example: FC<()> = |cx, props| { - let count = use_state(cx, || 0); - let mut direction = use_state(cx, || 1); +pub static Example: Component<()> = |cx| { + let count = use_state(&cx, || 0); + let mut direction = use_state(&cx, || 1); // Tasks are 'static, so we need to copy relevant items in let (async_count, dir) = (count.for_async(), *direction); diff --git a/examples/core_reference/testing.rs b/examples/core_reference/testing.rs index 587ce19c..ec10acaa 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: Component<()> = |cx| { cx.render(rsx! { div { diff --git a/examples/core_reference/tostring.rs b/examples/core_reference/tostring.rs index a04a165d..09f8d95d 100644 --- a/examples/core_reference/tostring.rs +++ b/examples/core_reference/tostring.rs @@ -1,13 +1,13 @@ use dioxus::prelude::*; use dioxus::ssr; -pub static Example: FC<()> = |cx, props| { - let as_string = use_state(cx, || { +pub static Example: Component<()> = |cx| { + let as_string = use_state(&cx, || { // Currently, SSR is only supported for whole VirtualDOMs // This is an easy/low hanging fruit to improve upon let mut dom = VirtualDom::new(SomeApp); dom.rebuild(); - ssr::render_vdom(&dom, |c| c) + ssr::render_vdom(&dom) }); cx.render(rsx! { @@ -15,7 +15,7 @@ pub static Example: FC<()> = |cx, props| { }) }; -static SomeApp: FC<()> = |cx, props| { +static SomeApp: Component<()> = |cx| { 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 61bed91c..e112c299 100644 --- a/examples/coroutine.rs +++ b/examples/coroutine.rs @@ -22,14 +22,14 @@ //! the coroutine was initiated. `use_state` always returns the same setter, so you don't need to worry about fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } use dioxus::prelude::*; -static App: Component<()> = |cx, props| { - let p1 = use_state(cx, || 0); - let p2 = use_state(cx, || 0); +static App: Component<()> = |cx| { + let p1 = use_state(&cx, || 0); + let p2 = use_state(&cx, || 0); let (mut p1_async, mut p2_async) = (p1.for_async(), p2.for_async()); let (p1_handle, _) = use_coroutine(cx, || async move { diff --git a/examples/crm.rs b/examples/crm.rs index f146e085..2ae7d3ee 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -4,7 +4,7 @@ Tiny CRM: A port of the Yew CRM example to Dioxus. use dioxus::prelude::*; fn main() { - dioxus::web::launch(App, |c| c); + dioxus::web::launch(App); } enum Scene { ClientsList, @@ -19,13 +19,13 @@ pub struct Client { pub description: String, } -static App: Component<()> = |cx, _| { - let mut clients = use_ref(cx, || vec![] as Vec); - let mut scene = use_state(cx, || Scene::ClientsList); +static App: Component<()> = |cx| { + let mut clients = use_ref(&cx, || vec![] as Vec); + let mut scene = use_state(&cx, || Scene::ClientsList); - let mut firstname = use_state(cx, || String::new()); - let mut lastname = use_state(cx, || String::new()); - let mut description = use_state(cx, || String::new()); + let mut firstname = use_state(&cx, || String::new()); + let mut lastname = use_state(&cx, || String::new()); + let mut description = use_state(&cx, || String::new()); let scene = match *scene { Scene::ClientsList => { diff --git a/examples/desktop/crm.rs b/examples/desktop/crm.rs index 8cc5b298..f216f884 100644 --- a/examples/desktop/crm.rs +++ b/examples/desktop/crm.rs @@ -5,7 +5,7 @@ use dioxus_hooks::*; use dioxus_html as dioxus_elements; fn main() { - dioxus_desktop::launch(App, |c| c) + dioxus_desktop::launch(App) } enum Scene { @@ -21,13 +21,13 @@ pub struct Client { pub description: String, } -static App: FC<()> = |cx, _| { - let mut scene = use_state(cx, || Scene::ClientsList); - let clients = use_ref(cx, || vec![] as Vec); +static App: Component<()> = |cx| { + let mut scene = use_state(&cx, || Scene::ClientsList); + let clients = use_ref(&cx, || vec![] as Vec); - let mut firstname = use_state(cx, String::new); - let mut lastname = use_state(cx, String::new); - let mut description = use_state(cx, String::new); + let mut firstname = use_state(&cx, String::new); + let mut lastname = use_state(&cx, String::new); + let mut description = use_state(&cx, String::new); let scene = match scene.get() { Scene::ClientsList => { diff --git a/examples/desktop/demo.rs b/examples/desktop/demo.rs index 77a927e9..d327b7f3 100644 --- a/examples/desktop/demo.rs +++ b/examples/desktop/demo.rs @@ -5,10 +5,10 @@ use dioxus_core_macro::*; use dioxus_html as dioxus_elements; fn main() { - dioxus_desktop::launch(App, |c| c); + dioxus_desktop::launch(App); } -static App: FC<()> = |cx, props| { +static App: Component<()> = |cx| { cx.render(rsx!( div { "hello world!" diff --git a/examples/desktop/todomvc.rs b/examples/desktop/todomvc.rs index d8edf40e..2674d9de 100644 --- a/examples/desktop/todomvc.rs +++ b/examples/desktop/todomvc.rs @@ -14,7 +14,7 @@ fn main() { SimpleLogger::new().init().unwrap(); } - dioxus_desktop::launch(App, |c| c) + dioxus_desktop::launch(App) } #[derive(PartialEq)] @@ -32,14 +32,14 @@ pub struct TodoItem { } pub type Todos = HashMap; -pub static App: FC<()> = |cx, _| { +pub static App: Component<()> = |cx| { // Share our TodoList to the todos themselves use_provide_state(cx, Todos::new); // Save state for the draft, filter - let draft = use_state(cx, || "".to_string()); - let filter = use_state(cx, || FilterState::All); - let mut todo_id = use_state(cx, || 0); + let draft = use_state(&cx, || "".to_string()); + let filter = use_state(&cx, || FilterState::All); + let mut todo_id = use_state(&cx, || 0); // Consume the todos let todos = use_shared_state::(cx)?; @@ -142,7 +142,7 @@ pub fn TodoEntry((cx, props): Scope) -> Element { let _todos = todos.read(); let todo = _todos.get(&props.id)?; - let is_editing = use_state(cx, || false); + let is_editing = use_state(&cx, || false); let completed = if todo.checked { "completed" } else { "" }; cx.render(rsx!{ diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 74ea6a7d..98b72989 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; fn main() { simple_logger::init_with_level(log::Level::Debug); - dioxus::desktop::launch(App, |c| { + dioxus::desktop::launch_cfg(App, |c| { c.with_window(|w| { w.with_resizable(true).with_inner_size( dioxus::desktop::wry::application::dpi::LogicalSize::new(400.0, 800.0), @@ -18,8 +18,8 @@ fn main() { }); } -static App: Component<()> = |cx, props| { - let file_manager = use_ref(cx, || Files::new()); +static App: Component<()> = |cx| { + let file_manager = use_ref(&cx, Files::new); let files = file_manager.read(); let file_list = files.path_names.iter().enumerate().map(|(dir_id, path)| { @@ -39,13 +39,15 @@ static App: Component<()> = |cx, props| { let current_dir = files.current(); - rsx!(cx, div { - h1 {"Files: "} - h3 {"Cur dir: {current_dir}"} - button { "go up", onclick: move |_| file_manager.write().go_up() } - ol { {file_list} } - {err_disp} - }) + cx.render(rsx!( + div { + h1 {"Files: "} + h3 {"Cur dir: {current_dir}"} + button { "go up", onclick: move |_| file_manager.write().go_up() } + ol { {file_list} } + {err_disp} + } + )) }; struct Files { diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index 44fe202e..fada80ad 100644 --- a/examples/framework_benchmark.rs +++ b/examples/framework_benchmark.rs @@ -2,8 +2,8 @@ use dioxus::prelude::*; use rand::prelude::*; fn main() { - dioxus::web::launch(App, |c| c); - // dioxus::desktop::launch(App, |c| c); + dioxus::web::launch(App); + // dioxus::desktop::launch(App); } #[derive(Clone, PartialEq)] @@ -30,9 +30,9 @@ impl Label { } } -static App: Component<()> = |cx, _props| { - let mut items = use_ref(cx, || vec![]); - let mut selected = use_state(cx, || None); +static App: Component<()> = |cx| { + let mut items = use_ref(&cx, || vec![]); + let mut selected = use_state(&cx, || None); cx.render(rsx! { div { class: "container" @@ -95,10 +95,10 @@ struct ActionButtonProps<'a> { onclick: &'a dyn Fn(), } -fn ActionButton(cx: Scope, props: &ActionButtonProps) -> Element { +fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element { rsx!(cx, div { class: "col-sm-6 smallpad" - button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: move |_| (props.onclick)(), - "{props.name}" + button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}", onclick: move |_| (props.onclick)(), + "{cx.props.name}" } }) } @@ -149,9 +149,9 @@ static NOUNS: &[&str] = &[ // fn Row(cx: Context, props: &RowProps) -> Element { // rsx!(cx, tr { -// td { class:"col-md-1", "{props.row_id}" } +// td { class:"col-md-1", "{cx.props.row_id}" } // td { class:"col-md-1", onclick: move |_| { /* run onselect */ } -// a { class: "lbl", {props.label.labels} } +// a { class: "lbl", {cx.props.label.labels} } // } // td { class: "col-md-1" // a { class: "remove", onclick: move |_| {/* remove */} diff --git a/examples/hello_world.rs b/examples/hello_world.rs index e6c874ba..f788f979 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } fn App((cx, props): ScopeState<()>) -> Element { diff --git a/examples/hydration.rs b/examples/hydration.rs index 22a81ffe..8189f7cd 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -19,8 +19,8 @@ fn main() { dioxus::desktop::launch(App, |c| c.with_prerendered(content)); } -static App: Component<()> = |cx, props| { - let mut val = use_state(cx, || 0); +static App: Component<()> = |cx| { + let mut val = use_state(&cx, || 0); cx.render(rsx! { div { diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index d0a9e729..f59a548c 100644 --- a/examples/pattern_model.rs +++ b/examples/pattern_model.rs @@ -16,13 +16,13 @@ //! RefMuts at the same time. use dioxus::desktop::wry::application::dpi::LogicalSize; -use dioxus::events::{on::*, KeyCode}; +use dioxus::events::*; use dioxus::prelude::*; const STYLE: &str = include_str!("./assets/calculator.css"); fn main() { env_logger::init(); - dioxus::desktop::launch(App, |cfg| { + dioxus::desktop::launch_cfg(App, |cfg| { cfg.with_window(|w| { w.with_title("Calculator Demo") .with_resizable(false) @@ -31,8 +31,8 @@ fn main() { }); } -static App: Component<()> = |cx, props| { - let state = use_ref(cx, || Calculator::new()); +static App: Component<()> = |cx| { + let state = use_ref(&cx, || Calculator::new()); let clear_display = state.read().display_value.eq("0"); let clear_text = if clear_display { "C" } else { "AC" }; @@ -80,8 +80,8 @@ struct CalculatorKeyProps<'a> { fn CalculatorKey<'a>((cx, props): ScopeState<'a, CalculatorKeyProps<'a>>) -> Element<'a> { cx.render(rsx! { button { - class: "calculator-key {props.name}" - onclick: {props.onclick} + class: "calculator-key {cx.props.name}" + onclick: {cx.props.onclick} {&props.children} } }) diff --git a/examples/pattern_reducer.rs b/examples/pattern_reducer.rs index 42c1976f..7441516a 100644 --- a/examples/pattern_reducer.rs +++ b/examples/pattern_reducer.rs @@ -8,11 +8,11 @@ use dioxus::prelude::*; fn main() { env_logger::init(); - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } -pub static App: Component<()> = |cx, _| { - let state = use_state(cx, PlayerState::new); +pub static App: Component<()> = |cx| { + let state = use_state(&cx, PlayerState::new); let is_playing = state.is_playing(); diff --git a/examples/readme.rs b/examples/readme.rs index afff68f8..aa4cd9a8 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -4,11 +4,11 @@ use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } -static App: Component<()> = |cx, props| { - let mut count = use_state(cx, || 0); +static App: Component<()> = |cx| { + let mut count = use_state(&cx, || 0); cx.render(rsx! { div { diff --git a/examples/router.rs b/examples/router.rs index 66b86f56..be055fe9 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -23,7 +23,7 @@ pub enum Route { NotFound, } -static App: Component<()> = |cx, props| { +static App: Component<()> = |cx| { let route = use_router(cx, Route::parse)?; cx.render(rsx! { diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index e27d4c82..9b6283d9 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -39,7 +39,7 @@ //! - Allow top-level fragments //! fn main() { - dioxus::desktop::launch(Example, |c| c); + dioxus::desktop::launch(Example); } /// When trying to return "nothing" to Dioxus, you'll need to specify the type parameter or Rust will be sad. @@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None; use baller::Baller; use dioxus::prelude::*; -pub static Example: Component<()> = |cx, props| { +pub static Example: Component<()> = |cx| { 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 e23251ad..542417c0 100644 --- a/examples/ssr.rs +++ b/examples/ssr.rs @@ -5,11 +5,11 @@ use dioxus::ssr; fn main() { let mut vdom = VirtualDom::new(App); - // vdom.rebuild_in_place().expect("Rebuilding failed"); - println!("{}", ssr::render_vdom(&vdom, |c| c)); + vdom.rebuild_in_place().expect("Rebuilding failed"); + println!("{}", ssr::render_vdom(&vdom)); } -static App: Component<()> = |cx, props| { +static App: Component<()> = |cx| { cx.render(rsx!( div { h1 { "Title" } @@ -21,6 +21,6 @@ static App: Component<()> = |cx, props| { struct MyProps<'a> { text: &'a str, } -fn App2(cx: Scope, props: &MyProps) -> Element { +fn App2<'a>(cx: Scope<'a, MyProps<'a>>) -> Element { None } diff --git a/examples/ssr/basic.rs b/examples/ssr/basic.rs index c89c425e..89b976d4 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: Component<()> = |cx| { cx.render(rsx!( div { class: "overflow-hidden" diff --git a/examples/ssr/tide.rs b/examples/ssr/tide.rs index d86fbdf5..d7255b91 100644 --- a/examples/ssr/tide.rs +++ b/examples/ssr/tide.rs @@ -26,7 +26,7 @@ fn main() {} // dom.rebuild(); // Ok(Response::builder(200) -// .body(format!("{}", dioxus_ssr::render_vdom(&dom, |c| c))) +// .body(format!("{}", dioxus_ssr::render_vdom(&dom))) // .content_type(tide::http::mime::HTML) // .build()) // }); @@ -42,8 +42,8 @@ fn main() {} // initial_name: String, // } -// static Example: FC = |cx, props| { -// let dispaly_name = use_state(cx, move || props.initial_name.clone()); +// static Example: Component = |cx| { +// let dispaly_name = use_state(&cx, move || props.initial_name.clone()); // cx.render(rsx! { // div { class: "py-12 px-4 text-center w-full max-w-2xl mx-auto", diff --git a/examples/ssr/tofile.rs b/examples/ssr/tofile.rs index e3091580..8440074e 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: Component<()> = |cx| { 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: Component<()> = |cx| { 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: Component<()> = |cx| { // 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: Component<()> = |cx| { // 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: Component<()> = |cx| { 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: Component<()> = |cx| { cx.render(rsx!( svg { fill: "none" diff --git a/examples/tailwind.rs b/examples/tailwind.rs index 555d13d5..70a0448f 100644 --- a/examples/tailwind.rs +++ b/examples/tailwind.rs @@ -14,7 +14,7 @@ fn main() { const STYLE: &str = "body {overflow:hidden;}"; -pub static App: Component<()> = |cx, props| { +pub static App: Component<()> = |cx| { cx.render(rsx!( div { class: "overflow-hidden" style { "{STYLE}" } @@ -30,7 +30,7 @@ pub static App: Component<()> = |cx, props| { )) }; -pub static Header: Component<()> = |cx, props| { +pub static Header: Component<()> = |cx| { cx.render(rsx! { div { header { class: "text-gray-400 bg-gray-900 body-font" @@ -56,7 +56,7 @@ pub static Header: Component<()> = |cx, props| { }) }; -pub static Hero: Component<()> = |cx, props| { +pub static Hero: Component<()> = |cx| { // cx.render(rsx! { section{ class: "text-gray-400 bg-gray-900 body-font" @@ -94,7 +94,7 @@ pub static Hero: Component<()> = |cx, props| { } }) }; -pub static Entry: Component<()> = |cx, props| { +pub static Entry: Component<()> = |cx| { // cx.render(rsx! { section{ class: "text-gray-400 bg-gray-900 body-font" @@ -107,7 +107,7 @@ pub static Entry: Component<()> = |cx, props| { }) }; -pub static StacksIcon: Component<()> = |cx, props| { +pub static StacksIcon: Component<()> = |cx| { cx.render(rsx!( svg { // xmlns: "http://www.w3.org/2000/svg" @@ -122,7 +122,7 @@ pub static StacksIcon: Component<()> = |cx, props| { } )) }; -pub static RightArrowIcon: Component<()> = |cx, props| { +pub static RightArrowIcon: Component<()> = |cx| { cx.render(rsx!( svg { fill: "none" diff --git a/examples/tasks.rs b/examples/tasks.rs index 699ae127..3b14faff 100644 --- a/examples/tasks.rs +++ b/examples/tasks.rs @@ -6,11 +6,11 @@ use std::time::Duration; use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } -static App: Component<()> = |cx, props| { - let mut count = use_state(cx, || 0); +static App: Component<()> = |cx| { + let mut count = use_state(&cx, || 0); cx.push_task(async move { tokio::time::sleep(Duration::from_millis(100)).await; diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 395b8669..b73d4ef4 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -4,7 +4,7 @@ use im_rc::HashMap; use std::rc::Rc; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } #[derive(PartialEq)] @@ -22,10 +22,10 @@ pub struct TodoItem { } const STYLE: &str = include_str!("./assets/todomvc.css"); -const App: Component<()> = |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); +const App: Component<()> = |cx| { + let mut draft = use_state(&cx, || "".to_string()); + let mut todos = use_state(&cx, || HashMap::>::new()); + let mut filter = use_state(&cx, || FilterState::All); let todolist = todos .iter() @@ -85,9 +85,9 @@ pub struct TodoEntryProps { todo: Rc, } -pub fn TodoEntry(cx: Scope, props: &TodoEntryProps) -> Element { - let mut is_editing = use_state(cx, || false); - let mut contents = use_state(cx, || String::from("")); +pub fn TodoEntry(cx: Scope) -> Element { + let mut is_editing = use_state(&cx, || false); + let mut contents = use_state(&cx, || String::from("")); let todo = &props.todo; rsx!(cx, li { diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 20fde8a2..0ba2ffa0 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -7,12 +7,12 @@ use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::desktop::launch(App); } const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather"; -static App: Component<()> = |cx, props| { +static App: Component<()> = |cx| { // let body = use_suspense( cx, @@ -40,7 +40,7 @@ static App: Component<()> = |cx, props| { #[derive(PartialEq, Props)] struct WeatherProps {} -static WeatherDisplay: Component = |cx, props| { +static WeatherDisplay: Component = |cx| { // cx.render(rsx!( div { class: "flex items-center justify-center flex-col" diff --git a/examples/web/async_web.rs b/examples/web/async_web.rs index a64ea059..a1a0f8bb 100644 --- a/examples/web/async_web.rs +++ b/examples/web/async_web.rs @@ -8,7 +8,7 @@ use dioxus_html as dioxus_elements; fn main() { console_error_panic_hook::set_once(); wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - dioxus_web::launch(APP, |c| c) + dioxus_web::launch(APP) } #[derive(serde::Deserialize)] @@ -16,8 +16,8 @@ struct DogApi { message: String, } -static APP: FC<()> = |(cx, _props)| { - let state = use_state(cx, || 0); +static APP: Component<()> = |(cx, _props)| { + let state = use_state(&cx, || 0); const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random/"; let doggo = use_suspense( diff --git a/examples/web/basic.rs b/examples/web/basic.rs index 4c4d52ce..ff70f8a1 100644 --- a/examples/web/basic.rs +++ b/examples/web/basic.rs @@ -11,13 +11,13 @@ fn main() { console_error_panic_hook::set_once(); // Run the app - dioxus_web::launch(APP, |c| c) + dioxus_web::launch(APP) } -static APP: FC<()> = |cx, _| { - let mut count = use_state(cx, || 3); - let content = use_state(cx, || String::from("h1")); - let text_content = use_state(cx, || String::from("Hello, world!")); +static APP: Component<()> = |cx| { + let mut count = use_state(&cx, || 3); + let content = use_state(&cx, || String::from("h1")); + let text_content = use_state(&cx, || String::from("Hello, world!")); cx.render(rsx! { div { @@ -86,4 +86,4 @@ fn render_list(cx: Context, count: usize) -> Element { rsx!(cx, ul { {items} }) } -static CHILD: FC<()> = |cx, _| rsx!(cx, div {"hello child"}); +static CHILD: Component<()> = |cx, _| rsx!(cx, div {"hello child"}); diff --git a/examples/web/blah.rs b/examples/web/blah.rs index d21cd5fd..5553ceed 100644 --- a/examples/web/blah.rs +++ b/examples/web/blah.rs @@ -21,11 +21,11 @@ fn main() { console_error_panic_hook::set_once(); // Run the app - dioxus_web::launch(App, |c| c) + dioxus_web::launch(App) } -static App: FC<()> = |cx, props| { - let mut state = use_state(cx, || 0); +static App: Component<()> = |cx| { + let mut state = use_state(&cx, || 0); cx.render(rsx! { div { style: { diff --git a/examples/web/btns.rs b/examples/web/btns.rs index 179074ee..a3c96ad7 100644 --- a/examples/web/btns.rs +++ b/examples/web/btns.rs @@ -18,12 +18,12 @@ use dioxus_html as dioxus_elements; // #[cfg] fn main() { wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - dioxus_web::launch(App, |c| c); + dioxus_web::launch(App); // env_logger::init(); - // dioxus::web::launch(App, |c| c); + // dioxus::web::launch(App); } -static App: FC<()> = |cx, props| { +static App: Component<()> = |cx| { dbg!("rednering parent"); cx.render(rsx! { div { @@ -40,8 +40,8 @@ static App: FC<()> = |cx, props| { }) }; -static But: FC<()> = |cx, props| { - let mut count = use_state(cx, || 0); +static But: Component<()> = |cx| { + let mut count = use_state(&cx, || 0); // let d = Dropper { name: "asd" }; // let handler = move |_| { diff --git a/examples/web/crm2.rs b/examples/web/crm2.rs index 4dfe9db3..4f0eff48 100644 --- a/examples/web/crm2.rs +++ b/examples/web/crm2.rs @@ -12,7 +12,7 @@ fn main() { wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); console_error_panic_hook::set_once(); - dioxus_web::launch(App, |c| c) + dioxus_web::launch(App) } enum Scene { @@ -28,13 +28,13 @@ pub struct Client { pub description: String, } -static App: FC<()> = |cx, _| { - let scene = use_state(cx, || Scene::ClientsList); - let clients = use_ref(cx, || vec![] as Vec); +static App: Component<()> = |cx| { + let scene = use_state(&cx, || Scene::ClientsList); + let clients = use_ref(&cx, || vec![] as Vec); - let firstname = use_state(cx, || String::new()); - let lastname = use_state(cx, || String::new()); - let description = use_state(cx, || String::new()); + let firstname = use_state(&cx, || String::new()); + let lastname = use_state(&cx, || String::new()); + let description = use_state(&cx, || String::new()); let scene = match *scene { Scene::ClientsList => { diff --git a/examples/web/demo.rs b/examples/web/demo.rs index 603b84de..6c21aa48 100644 --- a/examples/web/demo.rs +++ b/examples/web/demo.rs @@ -21,10 +21,10 @@ fn main() { console_error_panic_hook::set_once(); // Run the app - dioxus_web::launch(App, |c| c) + dioxus_web::launch(App) } -pub static App: FC<()> = |cx, props| { +pub static App: Component<()> = |cx| { 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: Component<()> = |cx| { 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: Component<()> = |cx| { // 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: Component<()> = |cx| { // 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: Component<()> = |cx| { 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: Component<()> = |cx| { cx.render(rsx!( svg { fill: "none" diff --git a/examples/web_tick.rs b/examples/web_tick.rs index 3a24ce52..098bfc1f 100644 --- a/examples/web_tick.rs +++ b/examples/web_tick.rs @@ -16,10 +16,10 @@ fn main() { #[cfg(target_arch = "wasm32")] intern_strings(); - dioxus::web::launch(App, |c| c); + dioxus::web::launch(App); } -static App: Component<()> = |cx, props| { +static App: Component<()> = |cx| { let mut rng = SmallRng::from_entropy(); let rows = (0..1_000).map(|f| { let label = Label::new(&mut rng); @@ -49,7 +49,7 @@ fn Row((cx, props): ScopeState) -> Element { let [adj, col, noun] = props.label.0; cx.render(rsx! { tr { - td { class:"col-md-1", "{props.row_id}" } + td { class:"col-md-1", "{cx.props.row_id}" } td { class:"col-md-1", onclick: move |_| { /* run onselect */ } a { class: "lbl", "{adj}" "{col}" "{noun}" } } diff --git a/examples/webview_web.rs b/examples/webview_web.rs index 55f9f430..a31096be 100644 --- a/examples/webview_web.rs +++ b/examples/webview_web.rs @@ -13,11 +13,11 @@ use dioxus::prelude::*; fn main() { - dioxus::web::launch(App, |c| c); + dioxus::web::launch(App); } -static App: Component<()> = |cx, props| { - let mut count = use_state(cx, || 0); +static App: Component<()> = |cx| { + let mut count = use_state(&cx, || 0); cx.render(rsx! { div { diff --git a/notes/Parity.md b/notes/Parity.md index 5cc0851c..fa51fff5 100644 --- a/notes/Parity.md +++ b/notes/Parity.md @@ -12,7 +12,7 @@ https://github.com/rustwasm/gloo For example, resize observer would function like this: ```rust -pub static Example: FC<()> = |cx, props|{ +pub static Example: Component<()> = |cx, props|{ let observer = use_resize_observer(); cx.render(rsx!( diff --git a/notes/SOLVEDPROBLEMS.md b/notes/SOLVEDPROBLEMS.md index 3c42094e..baf88701 100644 --- a/notes/SOLVEDPROBLEMS.md +++ b/notes/SOLVEDPROBLEMS.md @@ -153,13 +153,13 @@ Notice that LiveComponent receivers (the client-side interpretation of a LiveCom The `VNodeTree` type is a very special type that allows VNodes to be created using a pluggable allocator. The html! macro creates something that looks like: ```rust -pub static Example: FC<()> = |cx, props|{ +pub static Example: Component<()> = |cx, props|{ html! {
"blah"
} }; // expands to... -pub static Example: FC<()> = |cx, props|{ +pub static Example: Component<()> = |cx, props|{ // This function converts a Fn(allocator) -> DomTree closure to a VNode struct that will later be evaluated. html_macro_to_vnodetree(move |allocator| { let mut node0 = allocator.alloc(VElement::div); @@ -313,7 +313,7 @@ Here's how react does it: Any "dirty" node causes an entire subtree render. Calling "setState" at the very top will cascade all the way down. This is particularly bad for this component design: ```rust -static APP: FC<()> = |cx, props|{ +static APP: Component<()> = |cx, props|{ let title = use_context(Title); cx.render(html!{
@@ -334,7 +334,7 @@ static APP: FC<()> = |cx, props|{
}) }; -static HEAVY_LIST: FC<()> = |cx, props|{ +static HEAVY_LIST: Component<()> = |cx, props|{ cx.render({ {0.100.map(i => )} }) @@ -378,7 +378,7 @@ struct Props { } -static Component: FC = |cx, props|{ +static Component: Component = |cx, props|{ } ``` diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 6e754a16..6d36280c 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -30,7 +30,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token /// /// ## Complete Reference Guide: /// ``` -/// const Example: FC<()> = |cx, props|{ +/// const Example: Component<()> = |cx, props|{ /// let formatting = "formatting!"; /// let formatting_tuple = ("a", "b"); /// let lazy_fmt = format_args!("lazily formatted text"); diff --git a/packages/core-macro/src/rsx/ambiguous.rs b/packages/core-macro/src/rsx/ambiguous.rs index 71b3a2c4..16cb2c4e 100644 --- a/packages/core-macro/src/rsx/ambiguous.rs +++ b/packages/core-macro/src/rsx/ambiguous.rs @@ -43,7 +43,11 @@ impl Parse for AmbiguousElement { if first_char.is_ascii_uppercase() { input.parse::().map(AmbiguousElement::Component) } else { - input.parse::().map(AmbiguousElement::Element) + if input.peek2(syn::token::Paren) { + input.parse::().map(AmbiguousElement::Component) + } else { + input.parse::().map(AmbiguousElement::Element) + } } } else { Err(Error::new(input.span(), "Not a valid Html tag")) diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs index 23ff3b53..9c51cb74 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/packages/core-macro/src/rsx/component.rs @@ -39,7 +39,14 @@ impl Parse for Component { // parse the guts let content: ParseBuffer; - syn::braced!(content in stream); + + // if we see a `{` then we have a block + // else parse as a function-like call + if stream.peek(token::Brace) { + syn::braced!(content in stream); + } else { + syn::parenthesized!(content in stream); + } let cfg: BodyConfig = BodyConfig { allow_children: true, diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index deca59cd..1788427d 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -22,7 +22,7 @@ longest-increasing-subsequence = "0.1.0" # internall used log = { version = "0.4", features = ["release_max_level_off"] } -futures-util = { version = "0.3.15", default-features = false } +futures-util = { version = "0.3", default-features = false } smallvec = "1.6" @@ -48,7 +48,7 @@ fern = { version = "0.6.0", features = ["colored"] } rand = { version = "0.8.4", features = ["small_rng"] } simple_logger = "1.13.0" dioxus-core-macro = { path = "../core-macro", version = "0.1.2" } -dioxus-hooks = { path = "../hooks" } +# dioxus-hooks = { path = "../hooks" } criterion = "0.3.5" [features] diff --git a/packages/core/README.md b/packages/core/README.md index ce260200..71ee252c 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -2,7 +2,7 @@ This is the core crate for the Dioxus Virtual DOM. This README will focus on the technical design and layout of this Virtual DOM implementation. If you want to read more about using Dioxus, then check out the Dioxus crate, documentation, and website. -To build new apps with Dioxus or to extend the ecosystem with new hooks or components, use the `Dioxus` crate with the appropriate feature flags. +To build new apps with Dioxus or to extend the ecosystem with new hooks or components, use the higher-level `dioxus` crate with the appropriate feature flags. ## Internals @@ -10,69 +10,44 @@ Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus - React: hooks, concurrency, suspense - Dodrio: bump allocation, double buffering, and some diffing architecture -- Percy: html! macro architecture, platform-agnostic edits -- InfernoJS: approach to keyed diffing -- Preact: approach for normalization and ref -- Yew: passion and inspiration ❤️ Dioxus-core leverages some really cool techniques and hits a very high level of parity with mature frameworks. However, Dioxus also brings some new unique features: - managed lifetimes for borrowed data -- suspended nodes (task/fiber endpoints) for asynchronous vnodes +- placeholder approach for suspended vnodes +- fiber/interruptible diffing algorithm - custom memory allocator for vnodes and all text content - support for fragments w/ lazy normalization - slab allocator for scopes - mirrored-slab approach for remote vdoms +- dedicated subtrees for rendering into separate contexts from the same app -There's certainly more to the story, but these optimizations make Dioxus memory use and allocation count extremely minimal. For an average application, it is possible that zero allocations will need to be performed once the app has been mounted. Only when new components are added to the dom will allocations occur - and only en mass. The space of old VNodes is dynamically recycled as new nodes are added. Additionally, Dioxus tracks the average memory footprint of previous components to estimate how much memory allocate for future components. +There's certainly more to the story, but these optimizations make Dioxus memory use and allocation count extremely minimal. For an average application, it is possible that zero allocations will need to be performed once the app has been loaded. Only when new components are added to the dom will allocations occur. For a given component, the space of old VNodes is dynamically recycled as new nodes are added. Additionally, Dioxus tracks the average memory footprint of previous components to estimate how much memory allocate for future components. -All in all, Dioxus treats memory as an incredibly valuable resource. Combined with the memory-efficient footprint of Wasm compilation, Dioxus apps can scale to thousands of components and still stay snappy and respect your RAM usage. +All in all, Dioxus treats memory as a valuable resource. Combined with the memory-efficient footprint of Wasm compilation, Dioxus apps can scale to thousands of components and still stay snappy. ## Goals -We have big goals for Dioxus. The final implementation must: +The final implementation of Dioxus must: - Be **fast**. Allocators are typically slow in Wasm/Rust, so we should have a smart way of allocating. -- Be extremely memory efficient. Servers should handle tens of thousands of simultaneous VDoms with no problem. -- Be concurrent. Components should be able to pause rendering using a threading mechanism. -- Be "remote". Edit lists should be separate from the Renderer implementation. -- Support SSR. VNodes should render to a string that can be served via a web server. +- Be memory efficient. Servers should handle tens of thousands of simultaneous VDoms with no problem. +- Be concurrent. Components should be able to pause rendering to let the screen paint the next frame. +- Be disconnected from a specific renderer (no WebSys dependency in the core crate). +- Support server-side-rendering (SSR). VNodes should render to a string that can be served via a web server. - Be "live". Components should be able to be both server rendered and client rendered without needing frontend APIs. - Be modular. Components and hooks should be work anywhere without worrying about target platform. - ## Safety +Dioxus uses unsafe. The design of Dioxus *requires* unsafe (self-referential trees). + +All of our test suite passes MIRI without errors. + Dioxus deals with arenas, lifetimes, asynchronous tasks, custom allocators, pinning, and a lot more foundational low-level work that is very difficult to implement with 0 unsafe. If you don't want to use a crate that uses unsafe, then this crate is not for you. -however, we are always interested in decreasing the scope of the core VirtualDom to make it easier to review. +However, we are always interested in decreasing the scope of the core VirtualDom to make it easier to review. We'd be happy to welcome PRs that can eliminate unsafe code while still upholding the numerous variants required to execute certain features. -We'd also be happy to welcome PRs that can eliminate unsafe code while still upholding the numerous variants required to execute certain features. - -There's a few invariants that are very important: - -- References to `ScopeInner` and `Props` passed into components are *always* valid for as long as the component exists. Even if the scope backing is resized to fit more scopes, the scope has to stay the same place in memory. - - - -## Suspense - -Suspense is done through combinators on values. - -```rust -let name = get_name(cx).suspend(); - -rsx!( - div { - {name} - div { - div { - - } - } - } -) -``` diff --git a/packages/core/architecture.md b/packages/core/architecture.md index a3f468c2..f852fda3 100644 --- a/packages/core/architecture.md +++ b/packages/core/architecture.md @@ -1,5 +1,7 @@ # Dioxus Core Architecture: +This document is mostly a brain-dump on how things work. A lot of this information might be outdated. Certain features (priority queues, swim lanes) might not be implemented yet. + Main topics covered here: - Fiber, Concurrency, and Cooperative Scheduling - Suspense diff --git a/packages/core/benches/jsframework.rs b/packages/core/benches/jsframework.rs index 4f5f762d..34884983 100644 --- a/packages/core/benches/jsframework.rs +++ b/packages/core/benches/jsframework.rs @@ -23,7 +23,7 @@ criterion_group!(mbenches, create_rows); criterion_main!(mbenches); fn create_rows(c: &mut Criterion) { - static App: Component<()> = |cx, _| { + static App: Component<()> = |cx| { let mut rng = SmallRng::from_entropy(); let rows = (0..10_000_usize).map(|f| { let label = Label::new(&mut rng); @@ -53,11 +53,11 @@ struct RowProps { row_id: usize, label: Label, } -fn Row(cx: Scope, props: &RowProps) -> Element { - let [adj, col, noun] = props.label.0; +fn Row(cx: Scope) -> Element { + let [adj, col, noun] = cx.props.label.0; cx.render(rsx! { tr { - td { class:"col-md-1", "{props.row_id}" } + td { class:"col-md-1", "{cx.props.row_id}" } td { class:"col-md-1", onclick: move |_| { /* run onselect */ } a { class: "lbl", "{adj}" "{col}" "{noun}" } } diff --git a/packages/core/examples/borrowed.rs b/packages/core/examples/borrowed.rs index 71a4f2fb..784f4874 100644 --- a/packages/core/examples/borrowed.rs +++ b/packages/core/examples/borrowed.rs @@ -31,21 +31,3 @@ fn bleat() { let blah = String::from("asd"); eat(&blah); } - -// struct Lower {} - -// #[derive(Clone, Copy)] -// struct Upper {} -// impl std::ops::Deref for Upper { -// type Target = Lower; - -// fn deref(&self) -> &Self::Target { -// todo!() -// } -// } - -// fn mark(f: &Lower) {} -// fn bark() { -// let up = Upper {}; -// mark(&up); -// } diff --git a/packages/core/examples/hooks.rs b/packages/core/examples/hooks.rs index d4d217fd..969d9f3f 100644 --- a/packages/core/examples/hooks.rs +++ b/packages/core/examples/hooks.rs @@ -1,16 +1,10 @@ use dioxus::prelude::*; use dioxus_core as dioxus; use dioxus_core_macro::*; -use dioxus_hooks::use_state; use dioxus_html as dioxus_elements; fn main() {} fn App(cx: Scope<()>) -> Element { - let color = use_state(&cx, || "white"); - - cx.render(rsx!( - div { onclick: move |_| color.set("red"), "red" } - div { onclick: move |_| color.set("blue"), "blue" } - )) + todo!() } diff --git a/packages/core/flamegraph.svg b/packages/core/flamegraph.svg deleted file mode 100644 index 0699a198..00000000 --- a/packages/core/flamegraph.svg +++ /dev/null @@ -1,412 +0,0 @@ -Flame Graph Reset ZoomSearch jsframework-a8f4acf5955e8e7f`core::ptr::drop_in_place<dioxus_core::virtual_dom::VirtualDom> (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`core::ptr::drop_in_place<alloc::boxed::Box<dioxus_core::scopearena::ScopeArena>> (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`<bumpalo::Bump as core::ops::drop::Drop>::drop (1 samples, 1.82%)j..libsystem_malloc.dylib`free_medium (1 samples, 1.82%)l..libsystem_kernel.dylib`madvise (1 samples, 1.82%)l..libsystem_malloc.dylib`_malloc_zone_malloc (2 samples, 3.64%)libs..libsystem_malloc.dylib`szone_malloc_should_clear (2 samples, 3.64%)libs..libsystem_malloc.dylib`tiny_malloc_should_clear (2 samples, 3.64%)libs..libsystem_malloc.dylib`tiny_malloc_from_free_list (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_free_list_add_ptr (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::create_element_node (7 samples, 12.73%)jsframework-a8f4acf..jsframework-a8f4acf5955e8e7f`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (3 samples, 5.45%)jsframe..jsframework-a8f4acf5955e8e7f`alloc::raw_vec::finish_grow (3 samples, 5.45%)jsframe..libsystem_malloc.dylib`realloc (1 samples, 1.82%)l..libsystem_malloc.dylib`malloc_zone_realloc (1 samples, 1.82%)l..libsystem_malloc.dylib`szone_realloc (1 samples, 1.82%)l..libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::fin_head (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`<bumpalo::Bump as core::default::Default>::default (4 samples, 7.27%)jsframewor..jsframework-a8f4acf5955e8e7f`<bumpalo::Bump as core::ops::drop::Drop>::drop (1 samples, 1.82%)j..libsystem_malloc.dylib`free_tiny (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_free_no_lock (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_free_list_add_ptr (1 samples, 1.82%)l..libsystem_malloc.dylib`malloc_zone_realloc (1 samples, 1.82%)l..libsystem_platform.dylib`DYLD-STUB$$_platform_memmove (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`alloc::raw_vec::finish_grow (2 samples, 3.64%)jsfr..libsystem_malloc.dylib`realloc (2 samples, 3.64%)libs..libsystem_malloc.dylib`szone_size (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`bumpalo::Bump::with_capacity (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`bumpalo::Bump::with_capacity (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::scope::BumpFrame::new (3 samples, 5.45%)jsframe..libsystem_malloc.dylib`_malloc_zone_malloc (1 samples, 1.82%)l..libsystem_malloc.dylib`szone_malloc_should_clear (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_malloc_should_clear (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_malloc_from_free_list (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::new_with_key (14 samples, 25.45%)jsframework-a8f4acf5955e8e7f`dioxus_core:..jsframework-a8f4acf5955e8e7f`hashbrown::raw::RawTable<T,A>::insert (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::element (6 samples, 10.91%)jsframework-a8f4..jsframework-a8f4acf5955e8e7f`bumpalo::Bump::alloc_layout_slow (6 samples, 10.91%)jsframework-a8f4..libsystem_malloc.dylib`_malloc_zone_malloc (2 samples, 3.64%)libs..libsystem_malloc.dylib`szone_malloc_should_clear (2 samples, 3.64%)libs..libsystem_malloc.dylib`tiny_malloc_should_clear (2 samples, 3.64%)libs..libsystem_malloc.dylib`set_tiny_meta_header_in_use (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::raw_element (12 samples, 21.82%)jsframework-a8f4acf5955e8e7f`dioxu..jsframework-a8f4acf5955e8e7f`bumpalo::Bump::alloc_layout_slow (12 samples, 21.82%)jsframework-a8f4acf5955e8e7f`bumpa..libsystem_malloc.dylib`_malloc_zone_malloc (1 samples, 1.82%)l..libsystem_malloc.dylib`szone_malloc_should_clear (1 samples, 1.82%)l..libsystem_malloc.dylib`small_malloc_should_clear (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`<&T as core::fmt::Display>::fmt (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`bumpalo::collections::string::String::into_bump_str (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::text (3 samples, 5.45%)jsframe..jsframework-a8f4acf5955e8e7f`core::fmt::write (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`<&mut W as core::fmt::Write>::write_str (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::call (22 samples, 40.00%)jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::c..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::new::_{{closure}} (22 samples, 40.00%)jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::n..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::empty_cell (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::scope::Scope::render (23 samples, 41.82%)jsframework-a8f4acf5955e8e7f`dioxus_core::scope::Scope::renderlibsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::create_node (40 samples, 72.73%)jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::create_nodejsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::run_scope (24 samples, 43.64%)jsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::run_s..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::component::_{{closure}} (24 samples, 43.64%)jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::component..jsframework-a8f4acf5955e8e7f`jsframework::Row (24 samples, 43.64%)jsframework-a8f4acf5955e8e7f`jsframework::Rowlibsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::work (48 samples, 87.27%)jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::workjsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::mount (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`<core::option::Option<dioxus_core::lazynodes::LazyNodes> as dioxus_core::nodes::IntoVNode>::into_vnode (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::call (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::new::_{{closure}} (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::component (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`main (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`mainjsframework-a8f4acf5955e8e7f`std::rt::lang_start_internal (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`std::rt::lang_start_internaljsframework-a8f4acf5955e8e7f`std::rt::lang_start::_{{closure}} (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`std::rt::lang_start::_{{closure}}jsframework-a8f4acf5955e8e7f`std::sys_common::backtrace::__rust_begin_short_backtrace (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`std::sys_common::backtrace::__rust_begin_short_backtracejsframework-a8f4acf5955e8e7f`jsframework::main (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`jsframework::mainjsframework-a8f4acf5955e8e7f`criterion::Criterion<M>::bench_function (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`criterion::Criterion<M>::bench_functionjsframework-a8f4acf5955e8e7f`criterion::benchmark_group::BenchmarkGroup<M>::bench_function (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`criterion::benchmark_group::BenchmarkGroup<M>::bench_functionjsframework-a8f4acf5955e8e7f`criterion::routine::Routine::test (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`criterion::routine::Routine::testjsframework-a8f4acf5955e8e7f`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iterjsframework-a8f4acf5955e8e7f`criterion::bencher::Bencher<M>::iter (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`criterion::bencher::Bencher<M>::iterjsframework-a8f4acf5955e8e7f`dioxus_core::virtual_dom::VirtualDom::rebuild (50 samples, 90.91%)jsframework-a8f4acf5955e8e7f`dioxus_core::virtual_dom::VirtualDom::rebuildjsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::run_scope (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::virtual_dom::VirtualDom::new_with_props_and_scheduler::_{{closure}} (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`core::ops::function::FnOnce::call_once (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::scope::Scope::render (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::call (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::new::_{{closure}} (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::fragment_from_iter (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`core::ops::function::impls::_<impl core::ops::function::FnOnce<A> for &mut F>::call_once (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`rand::rng::Rng::gen_range (1 samples, 1.82%)j..all (55 samples, 100%)0x1 (55 samples, 100.00%)0x1libdyld.dylib`start (55 samples, 100.00%)libdyld.dylib`startlibsystem_kernel.dylib`__exit (4 samples, 7.27%)libsystem_.. \ No newline at end of file diff --git a/packages/core/src/component.rs b/packages/core/src/component.rs index f8d3c972..75a3d4e1 100644 --- a/packages/core/src/component.rs +++ b/packages/core/src/component.rs @@ -58,7 +58,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> { /// cx.render(rsx!{ /// div { /// h1 {"Title card"} -/// {props.children} +/// {cx.props.children} /// } /// }) /// } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 35f7470b..5783a696 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -440,7 +440,7 @@ impl<'bump> DiffState<'bump> { let height = parent_scope.height + 1; let subtree = parent_scope.subtree.get(); - let parent_scope = unsafe { self.scopes.get_scope_raw(parent_idx) }; + let parent_scope = self.scopes.get_scope_raw(parent_idx); let caller = unsafe { std::mem::transmute(vcomponent.caller as *const _) }; let fc_ptr = vcomponent.user_fc; @@ -716,13 +716,12 @@ impl<'bump> DiffState<'bump> { new.associated_scope.set(Some(scope_addr)); // make sure the component's caller function is up to date - let scope = unsafe { - self.scopes - .get_scope_mut(scope_addr) - .unwrap_or_else(|| panic!("could not find {:?}", scope_addr)) - }; + let scope = self + .scopes + .get_scope(scope_addr) + .unwrap_or_else(|| panic!("could not find {:?}", scope_addr)); - scope.caller = unsafe { std::mem::transmute(new.caller) }; + scope.caller.set(unsafe { std::mem::transmute(new.caller) }); // React doesn't automatically memoize, but we do. let props_are_the_same = old.comparator.unwrap(); diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 9514f83f..1f8b8b1c 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -520,8 +520,8 @@ impl<'a> NodeFactory<'a> { move |other: &VComponent| { if user_fc == other.user_fc { // Safety - // - We guarantee that FC

is the same by function pointer - // - Because FC

is the same, then P must be the same (even with generics) + // - We guarantee that Component

is the same by function pointer + // - Because Component

is the same, then P must be the same (even with generics) // - Non-static P are autoderived to memoize as false // - This comparator is only called on a corresponding set of bumpframes // diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 5d07b392..6289811e 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -28,7 +28,7 @@ use bumpalo::{boxed::Box as BumpBox, Bump}; /// } /// /// fn Example(cx: Context, props: &ExampleProps) -> Element { -/// cx.render(rsx!{ div {"Hello, {props.name}"} }) +/// cx.render(rsx!{ div {"Hello, {cx.props.name}"} }) /// } /// ``` pub struct Scope<'a, P> { @@ -88,7 +88,7 @@ pub struct ScopeState { pub(crate) frames: [BumpFrame; 2], - pub(crate) caller: *const dyn Fn(&ScopeState) -> Element, + pub(crate) caller: Cell<*const dyn Fn(&ScopeState) -> Element>, pub(crate) items: RefCell>, @@ -269,12 +269,12 @@ impl ScopeState { /// ```rust, ignore /// struct SharedState(&'static str); /// - /// static App: FC<()> = |cx, props|{ + /// static App: Component<()> = |cx, props|{ /// cx.use_hook(|_| cx.provide_state(SharedState("world")), |_| {}, |_| {}); /// rsx!(cx, Child {}) /// } /// - /// static Child: FC<()> = |cx, props| { + /// static Child: Component<()> = |cx| { /// let state = cx.consume_state::(); /// rsx!(cx, div { "hello {state.0}" }) /// } @@ -344,7 +344,7 @@ impl ScopeState { /// ## Example /// /// ```ignore - /// fn Component(cx: Scope, props: &Props) -> Element { + /// fn Component(cx: Scope) -> Element { /// // Lazy assemble the VNode tree /// let lazy_nodes = rsx!("hello world"); /// diff --git a/packages/core/src/scopearena.rs b/packages/core/src/scopearena.rs index 9716a3ae..9afb8086 100644 --- a/packages/core/src/scopearena.rs +++ b/packages/core/src/scopearena.rs @@ -72,14 +72,10 @@ impl ScopeArena { unsafe { self.scopes.borrow().get(&id).map(|f| &**f) } } - pub(crate) unsafe fn get_scope_raw(&self, id: ScopeId) -> Option<*mut ScopeState> { + pub(crate) fn get_scope_raw(&self, id: ScopeId) -> Option<*mut ScopeState> { self.scopes.borrow().get(&id).copied() } - pub(crate) unsafe fn get_scope_mut(&self, id: ScopeId) -> Option<&mut ScopeState> { - self.scopes.borrow().get(&id).map(|s| &mut **s) - } - pub(crate) fn new_with_key( &self, fc_ptr: *const (), @@ -95,35 +91,13 @@ impl ScopeArena { if let Some(old_scope) = self.free_scopes.borrow_mut().pop() { let scope = unsafe { &mut *old_scope }; - scope.caller = caller; + scope.caller.set(caller); scope.parent_scope = parent_scope; scope.height = height; scope.subtree = Cell::new(subtree); scope.our_arena_idx = new_scope_id; scope.container = container; - // scope.frames[0].nodes.get_mut().push({ - // let vnode = scope.frames[0] - // .bump - // .alloc(VNode::Text(scope.frames[0].bump.alloc(VText { - // dom_id: Default::default(), - // is_static: false, - // text: "", - // }))); - // unsafe { std::mem::transmute(vnode as *mut VNode) } - // }); - - // scope.frames[1].nodes.get_mut().push({ - // let vnode = scope.frames[1] - // .bump - // .alloc(VNode::Text(scope.frames[1].bump.alloc(VText { - // dom_id: Default::default(), - // is_static: false, - // text: "", - // }))); - // unsafe { std::mem::transmute(vnode as *mut VNode) } - // }); - let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope); debug_assert!(any_item.is_none()); } else { @@ -148,7 +122,7 @@ impl ScopeArena { subtree: Cell::new(subtree), is_subtree_root: Cell::new(false), - caller, + caller: Cell::new(caller), generation: 0.into(), shared_contexts: Default::default(), @@ -277,18 +251,20 @@ impl ScopeArena { // Remove all the outdated listeners self.ensure_drop_safety(id); - let scope = unsafe { &mut *self.get_scope_mut(id).expect("could not find scope") }; + // todo: we *know* that this is aliased by the contents of the scope itself + let scope = unsafe { &mut *self.get_scope_raw(id).expect("could not find scope") }; // Safety: // - We dropped the listeners, so no more &mut T can be used while these are held // - All children nodes that rely on &mut T are replaced with a new reference scope.hook_idx.set(0); - // Safety: - // - We've dropped all references to the wip bump frame with "ensure_drop_safety" - unsafe { scope.reset_wip_frame() }; - + // book keeping to ensure safety around the borrowed data { + // Safety: + // - We've dropped all references to the wip bump frame with "ensure_drop_safety" + unsafe { scope.reset_wip_frame() }; + let mut items = scope.items.borrow_mut(); // just forget about our suspended nodes while we're at it @@ -298,12 +274,9 @@ impl ScopeArena { debug_assert!(items.listeners.is_empty()); debug_assert!(items.borrowed_props.is_empty()); debug_assert!(items.tasks.is_empty()); - - // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. - // scope.wip_frame().nodes } - let render: &dyn Fn(&ScopeState) -> Element = unsafe { &*scope.caller }; + let render: &dyn Fn(&ScopeState) -> Element = unsafe { &*scope.caller.get() }; if let Some(node) = render(scope) { if !scope.items.borrow().tasks.is_empty() { @@ -319,6 +292,10 @@ impl ScopeArena { scope.cycle_frame(); true } else { + // the component bailed out early + // this leaves the wip frame and all descendents in a state where + // their WIP frames are invalid + // todo: descendents should not cause the app to crash false } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 63d64efc..23d7570c 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -25,7 +25,7 @@ use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc, task::Poll}; /// /// fn App(cx: Context, props: &AppProps) -> Element { /// cx.render(rsx!( -/// div {"hello, {props.title}"} +/// div {"hello, {cx.props.title}"} /// )) /// } /// ``` @@ -36,7 +36,7 @@ use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc, task::Poll}; /// fn App(cx: Context, props: &AppProps) -> Element { /// cx.render(rsx!( /// NavBar { routes: ROUTES } -/// Title { "{props.title}" } +/// Title { "{cx.props.title}" } /// Footer {} /// )) /// } @@ -459,7 +459,7 @@ impl VirtualDom { /// /// # Example /// ```rust, ignore - /// static App: FC<()> = |cx, props| cx.render(rsx!{ "hello world" }); + /// static App: Component<()> = |cx, props| cx.render(rsx!{ "hello world" }); /// let mut dom = VirtualDom::new(); /// let edits = dom.rebuild(); /// @@ -497,7 +497,7 @@ impl VirtualDom { /// value: Shared<&'static str>, /// } /// - /// static App: FC = |cx, props|{ + /// static App: Component = |cx, props|{ /// let val = cx.value.borrow(); /// cx.render(rsx! { div { "{val}" } }) /// }; diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index d3976fde..2da5b5bd 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -7,7 +7,6 @@ use dioxus_core::DomEdit::*; use dioxus_core::ScopeId; use dioxus_core_macro::*; -use dioxus_hooks::*; use dioxus_html as dioxus_elements; use std::sync::{Arc, Mutex}; diff --git a/packages/desktop/README.md b/packages/desktop/README.md index 0c166348..18225ade 100644 --- a/packages/desktop/README.md +++ b/packages/desktop/README.md @@ -4,11 +4,11 @@ This crate provides an ergonomic API for Dioxus to build desktop apps. ```rust fn main() { - dioxus::desktop::launch(App, |c| c) + dioxus::desktop::launch(App) } -static App: FC<()> = |cx, props| { - let (count, set_count) = use_state(cx, || 0); +static App: Component<()> = |cx| { + let (count, set_count) = use_state(&cx, || 0); cx.render(rsx!( WebviewWindow { @@ -34,7 +34,7 @@ Window management, system trays, notifications, and other desktop-related functi Managing windows is done by simply rendering content into a `WebviewWindow` component. ```rust -static App: FC<()> = |cx, props| { +static App: Component<()> = |cx| { rsx!(cx, WebviewWindow { "hello world" } ) } ``` @@ -46,7 +46,7 @@ Notifications also use a declarative approach. Sending a notification has never The api has been somewhat modeled after https://github.com/mikaelbr/node-notifier ```rust -static Notifications: FC<()> = |cx, props| { +static Notifications: Component<()> = |cx| { cx.render(rsx!( Notification { title: "title" @@ -78,7 +78,7 @@ static Notifications: FC<()> = |cx, props| { Dioxus Desktop supports app trays, which can be built with native menu groups or with a custom window. ```rust -static Tray: FC<()> = |cx, props| { +static Tray: Component<()> = |cx| { cx.render(rsx!( GlobalTray { MenuGroup { @@ -90,7 +90,7 @@ static Tray: FC<()> = |cx, props| { }; // using a builder -static Tray: FC<()> = |cx, props| { +static Tray: Component<()> = |cx| { let menu = MenuGroup::builder(cx) .with_items([ MenuGroupItem::builder() @@ -107,7 +107,7 @@ static Tray: FC<()> = |cx, props| { } // or with a custom window -static Tray: FC<()> = |cx, props| { +static Tray: Component<()> = |cx| { rsx!(cx, GlobalTray { div { "custom buttons here" } }) }; ``` @@ -116,7 +116,7 @@ static Tray: FC<()> = |cx, props| { Declaring menus is convenient and cross-platform. ```rust -static Menu: FC<()> = |cx, props| { +static Menu: Component<()> = |cx| { cx.render(rsx!( MenuBarMajorItem { title: "File" MenuGroup { diff --git a/packages/desktop/examples/async.rs b/packages/desktop/examples/async.rs index b86b66df..2626355e 100644 --- a/packages/desktop/examples/async.rs +++ b/packages/desktop/examples/async.rs @@ -12,11 +12,11 @@ use dioxus_html as dioxus_elements; fn main() { simple_logger::init().unwrap(); - dioxus_desktop::launch(App, |c| c); + dioxus_desktop::launch(app); } -static App: Component<()> = |cx, props| { - let mut count = use_state(cx, || 0); +fn app(cx: Scope<()>) -> Element { + let mut count = use_state(&cx, || 0); log::debug!("count is {:?}", count); cx.push_task(|| async move { diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index b6e8ac4c..9a060721 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -90,7 +90,7 @@ fn syntax_works() { use dioxus_hooks::*; use dioxus_html as dioxus_elements; - static App: FC<()> = |cx, props| { + static App: Component<()> = |cx| { cx.render(rsx! { // left window WebviewWindow { diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index df97b49a..59654aeb 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -6,6 +6,9 @@ mod cfg; mod escape; mod events; +pub use wry; +pub use wry::application as tao; + // mod desktop_context; use cfg::DesktopConfig; @@ -28,7 +31,10 @@ use wry::{ webview::{WebView, WebViewBuilder}, }; -pub fn launch( +pub fn launch(root: Component<()>) { + launch_with_props(root, (), |c| c) +} +pub fn launch_cfg( root: Component<()>, config_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>, ) { diff --git a/packages/hooks/Cargo.toml b/packages/hooks/Cargo.toml index 2327308b..93133865 100644 --- a/packages/hooks/Cargo.toml +++ b/packages/hooks/Cargo.toml @@ -1,11 +1,10 @@ [package] name = "dioxus-hooks" -version = "0.0.0" +version = "0.1.3" authors = ["Jonathan Kelley "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../../packages/core", version = "0.1.3" } -futures = "0.3" +dioxus-core = { path = "../../packages/core" } diff --git a/packages/hooks/README.md b/packages/hooks/README.md index 39ab816b..11e008c4 100644 --- a/packages/hooks/README.md +++ b/packages/hooks/README.md @@ -16,10 +16,10 @@ You can always use it "normally" with the `split` method: ```rust // Normal usage: -let value = use_state(cx, || 10); +let value = use_state(&cx, || 10); // "Classic" usage: -let (value, set_value) = use_state(cx, || 0).split(); +let (value, set_value) = use_state(&cx, || 0).split(); ``` ## use_ref diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index f6a5227b..ef5b699b 100644 --- a/packages/hooks/src/lib.rs +++ b/packages/hooks/src/lib.rs @@ -1,9 +1,6 @@ mod usestate; pub use usestate::{use_state, AsyncUseState, UseState}; -mod usestate2; -// pub use usestate2::use_state2; - mod useref; pub use useref::*; diff --git a/packages/hooks/src/usecollection.rs b/packages/hooks/src/usecollection.rs index d0b3b71e..47005e57 100644 --- a/packages/hooks/src/usecollection.rs +++ b/packages/hooks/src/usecollection.rs @@ -16,7 +16,7 @@ This is moderately efficient because the fields of the map are moved, but the da However, if you used similar approach with Dioxus: ```rust -let (map, set_map) = use_state(cx, || HashMap::new()); +let (map, set_map) = use_state(&cx, || HashMap::new()); set_map({ let mut newmap = map.clone(); newmap.set(key, value); @@ -36,7 +36,7 @@ uses the same memoization on top of the use_context API. Here's a fully-functional todo app using the use_map API: ```rust -static TodoList: FC<()> = |cx, props|{ +static TodoList: Component<()> = |cx, props|{ let todos = use_map(cx, || HashMap::new()); let input = use_ref(|| None); diff --git a/packages/hooks/src/usecoroutine.rs b/packages/hooks/src/usecoroutine.rs index f04fb4f1..f593c2b7 100644 --- a/packages/hooks/src/usecoroutine.rs +++ b/packages/hooks/src/usecoroutine.rs @@ -1,5 +1,5 @@ use dioxus_core::ScopeState; -use futures::Future; +use std::future::Future; use std::{ cell::{Cell, RefCell}, pin::Pin, diff --git a/packages/hooks/src/usemodel.rs b/packages/hooks/src/usemodel.rs index 47981af0..642081cf 100644 --- a/packages/hooks/src/usemodel.rs +++ b/packages/hooks/src/usemodel.rs @@ -5,9 +5,9 @@ //! In these cases, we provide `use_model` - a convenient way of abstracting over some state and async functions. use dioxus_core::prelude::ScopeState; -use futures::Future; use std::{ cell::{Cell, Ref, RefCell, RefMut}, + future::Future, marker::PhantomData, pin::Pin, rc::Rc, diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 0c5bb708..1c4fe2d0 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -39,7 +39,7 @@ impl<'a, P, T> UseStateA<'a, T> for Scope<'a, P> { /// On top of the methods to set/get state, `use_state` also supports fancy combinators to extend its functionality: /// - `.classic()` and `.split()` convert the hook into the classic React-style hook /// ```rust -/// let (state, set_state) = use_state(cx, || 10).split() +/// let (state, set_state) = use_state(&cx, || 10).split() /// ``` /// /// diff --git a/packages/hooks/src/usestate2.rs b/packages/hooks/src/usestate2.rs deleted file mode 100644 index 6cffc8e9..00000000 --- a/packages/hooks/src/usestate2.rs +++ /dev/null @@ -1,227 +0,0 @@ -// use dioxus_core::prelude::Context; -// use std::{ -// borrow::{Borrow, BorrowMut}, -// cell::{Cell, Ref, RefCell, RefMut}, -// fmt::{Debug, Display}, -// ops::Not, -// rc::Rc, -// }; - -// /// Store state between component renders! -// /// -// /// ## Dioxus equivalent of UseStateInner2, designed for Rust -// /// -// /// The Dioxus version of `UseStateInner2` is the "king daddy" of state management. It allows you to ergonomically store and -// /// modify state between component renders. When the state is updated, the component will re-render. -// /// -// /// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system. -// /// -// /// [`use_state`] exposes a few helper methods to modify the underlying state: -// /// - `.set(new)` allows you to override the "work in progress" value with a new value -// /// - `.get_mut()` allows you to modify the WIP value -// /// - `.get_wip()` allows you to access the WIP value -// /// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required) -// /// -// /// Additionally, a ton of std::ops traits are implemented for the `UseStateInner2` wrapper, meaning any mutative type operations -// /// will automatically be called on the WIP value. -// /// -// /// ## Combinators -// /// -// /// On top of the methods to set/get state, `use_state` also supports fancy combinators to extend its functionality: -// /// - `.classic()` and `.split()` convert the hook into the classic React-style hook -// /// ```rust -// /// let (state, set_state) = use_state(cx, || 10).split() -// /// ``` -// /// -// /// -// /// Usage: -// /// ```ignore -// /// const Example: FC<()> = |cx, props|{ -// /// let counter = use_state(cx, || 0); -// /// let increment = |_| counter += 1; -// /// let decrement = |_| counter += 1; -// /// -// /// html! { -// ///

-// ///

"Counter: {counter}"

-// /// -// /// -// ///
-// /// } -// /// } -// /// ``` -// pub fn use_state2<'a, T: 'static>( -// cx: Context<'a>, -// initial_state_fn: impl FnOnce() -> T, -// ) -> &'a UseState2 { -// cx.use_hook( -// move |_| { -// UseState2(Rc::new(UseStateInner2 { -// current_val: initial_state_fn(), -// update_callback: cx.schedule_update(), -// wip: None, -// update_scheuled: Cell::new(false), -// })) -// }, -// move |hook: &mut UseState2| { -// { -// let r = hook.0.as_ref(); -// let mut state = r.borrow_mut(); -// state.update_scheuled.set(false); -// if state.wip.is_some() { -// state.current_val = state.wip.take().unwrap(); -// } -// } -// &*hook -// }, -// ) -// } - -// pub struct UseState2(Rc>); - -// impl ToOwned for UseState2 { -// type Owned = UseState2; -// fn to_owned(&self) -> Self::Owned { -// UseState2(self.0.clone()) -// } -// } - -// pub struct UseStateInner2 { -// current_val: T, -// update_scheuled: Cell, -// update_callback: Rc, -// wip: Option, -// } - -// impl Debug for UseStateInner2 { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// write!(f, "{:?}", self.current_val) -// } -// } - -// impl UseState2 { -// /// Tell the Dioxus Scheduler that we need to be processed -// pub fn needs_update(&self) { -// if !self.update_scheuled.get() { -// self.update_scheuled.set(true); -// (self.update_callback)(); -// } -// } - -// pub fn set(&mut self, new_val: T) -> Option { -// self.needs_update(); -// ip.wip.replace(new_val) -// } - -// pub fn get(&self) -> &T { -// &self.current_val -// } -// } - -// // impl> UseState2 { -// // /// Gain mutable access to the new value. This method is only available when the value is a `ToOwned` type. -// // /// -// // /// Mutable access is derived by calling "ToOwned" (IE cloning) on the current value. -// // /// -// // /// To get a reference to the current value, use `.get()` -// // pub fn modify(&self) -> RefMut { -// // // make sure we get processed -// // self.0.needs_update(); - -// // // Bring out the new value, cloning if it we need to -// // // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this -// // RefMut::map(self.wip.borrow_mut(), |slot| { -// // if slot.is_none() { -// // *slot = Some(self.current_val.to_owned()); -// // } -// // slot.as_mut().unwrap() -// // }) -// // } - -// // pub fn inner(self) -> T { -// // self.current_val.to_owned() -// // } -// // } - -// impl std::ops::Deref for UseStateInner2 { -// type Target = T; - -// fn deref(&self) -> &Self::Target { -// self.get() -// } -// } - -// use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; - -// use crate::UseState; - -// impl> Add for UseStateInner2 { -// type Output = T; - -// fn add(self, rhs: T) -> Self::Output { -// self.current_val.add(rhs) -// } -// } -// impl> AddAssign for UseStateInner2 { -// fn add_assign(&mut self, rhs: T) { -// self.set(self.current_val.add(rhs)); -// } -// } -// impl> Sub for UseStateInner2 { -// type Output = T; - -// fn sub(self, rhs: T) -> Self::Output { -// self.current_val.sub(rhs) -// } -// } -// impl> SubAssign for UseStateInner2 { -// fn sub_assign(&mut self, rhs: T) { -// self.set(self.current_val.sub(rhs)); -// } -// } - -// /// MUL -// impl> Mul for UseStateInner2 { -// type Output = T; - -// fn mul(self, rhs: T) -> Self::Output { -// self.current_val.mul(rhs) -// } -// } -// impl> MulAssign for UseStateInner2 { -// fn mul_assign(&mut self, rhs: T) { -// self.set(self.current_val.mul(rhs)); -// } -// } -// /// DIV -// impl> Div for UseStateInner2 { -// type Output = T; - -// fn div(self, rhs: T) -> Self::Output { -// self.current_val.div(rhs) -// } -// } -// impl> DivAssign for UseStateInner2 { -// fn div_assign(&mut self, rhs: T) { -// self.set(self.current_val.div(rhs)); -// } -// } -// impl> PartialEq for UseStateInner2 { -// fn eq(&self, other: &V) -> bool { -// self.get() == other -// } -// } -// impl + Copy> Not for UseStateInner2 { -// type Output = O; - -// fn not(self) -> Self::Output { -// !*self.get() -// } -// } - -// // enable displaty for the handle -// impl std::fmt::Display for UseStateInner2 { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// write!(f, "{}", self.current_val) -// } -// } diff --git a/packages/hooks/src/usetask.rs b/packages/hooks/src/usetask.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/liveview/README.md b/packages/liveview/README.md index 1e29ac75..4a05d9f1 100644 --- a/packages/liveview/README.md +++ b/packages/liveview/README.md @@ -40,7 +40,7 @@ async fn order_shoes(mut req: WebsocketRequest) -> Response { } fn App(cx: Context, props: &()) -> Element { - let mut count = use_state(cx, || 0); + let mut count = use_state(&cx, || 0); cx.render(rsx!( button { onclick: move |_| count += 1, "Incr" } button { onclick: move |_| count -= 1, "Decr" } diff --git a/packages/mobile/src/lib.rs b/packages/mobile/src/lib.rs index 6d7f2711..e11765dd 100644 --- a/packages/mobile/src/lib.rs +++ b/packages/mobile/src/lib.rs @@ -69,7 +69,7 @@ impl WebviewRenderer { redits: Option>>, ) -> anyhow::Result<()> { // pub fn run_with_edits( - // root: FC, + // root: Component, // props: T, // user_builder: fn(WindowBuilder) -> WindowBuilder, // redits: Option>>, diff --git a/packages/router/README.md b/packages/router/README.md index 598c4f6a..05181ec9 100644 --- a/packages/router/README.md +++ b/packages/router/README.md @@ -16,7 +16,7 @@ enum AppRoute { NotFound } -static App: FC<()> = |cx, props| { +static App: Component<()> = |cx| { let route = use_router(cx, AppRoute::parse); match route { @@ -30,7 +30,7 @@ static App: FC<()> = |cx, props| { Adding links into your app: ```rust -static Leaf: FC<()> = |cx, props| { +static Leaf: Component<()> = |cx| { rsx!(cx, div { Link { to: AppRoute::Home } }) diff --git a/packages/router/examples/simple.rs b/packages/router/examples/simple.rs index 6c604b88..5f810522 100644 --- a/packages/router/examples/simple.rs +++ b/packages/router/examples/simple.rs @@ -6,10 +6,10 @@ use dioxus_router::*; fn main() { console_error_panic_hook::set_once(); wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - dioxus_web::launch(App, |c| c); + dioxus_web::launch(APP); } -static App: Component<()> = |cx, props| { +static APP: Component<()> = |cx| { #[derive(Clone, Debug, PartialEq)] enum Route { Home, @@ -17,7 +17,7 @@ static App: Component<()> = |cx, props| { NotFound, } - let route = use_router(cx, |s| match s { + let route = use_router(&cx, |s| match s { "/" => Route::Home, "/about" => Route::About, _ => Route::NotFound, diff --git a/packages/ssr/README.md b/packages/ssr/README.md index 4d47b426..02f6e09d 100644 --- a/packages/ssr/README.md +++ b/packages/ssr/README.md @@ -5,7 +5,7 @@ Render a Dioxus VirtualDOM to a string. ```rust // Our app: -const App: FC<()> = |cx, props| rsx!(cx, div {"hello world!"}); +const App: Component<()> = |cx, props| rsx!(cx, div {"hello world!"}); // Build the virtualdom from our app let mut vdom = VirtualDOM::new(App); @@ -14,7 +14,7 @@ let mut vdom = VirtualDOM::new(App); let _ = vdom.rebuild(); // Render the entire virtualdom from the root -let text = dioxus::ssr::render_vdom(&vdom, |c| c); +let text = dioxus::ssr::render_vdom(&vdom); assert_eq!(text, "
hello world!
") ``` @@ -30,7 +30,7 @@ With pre-rendering enabled, this crate will generate element nodes with Element To enable pre-rendering, simply configure the `SsrConfig` with pre-rendering enabled. ```rust -let dom = VirtualDom::new(App, |c| c); +let dom = VirtualDom::new(App); let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true)); ``` @@ -40,7 +40,7 @@ let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true)); Dioxus SSR can also be to render on the server. Obviously, you can just render the VirtualDOM to a string and send that down. ```rust -let text = dioxus::ssr::render_vdom(&vdom, |c| c); +let text = dioxus::ssr::render_vdom(&vdom); assert_eq!(text, "
hello world!
") ``` diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index 80de9268..a29a7db1 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -82,7 +82,10 @@ pub fn render_lazy<'a>(f: Option>) -> String { ) } -pub fn render_vdom(dom: &VirtualDom, cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> String { +pub fn render_vdom(dom: &VirtualDom) -> String { + format!("{:}", TextRenderer::from_vdom(dom, SsrConfig::default())) +} +pub fn render_vdom_cfg(dom: &VirtualDom, cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> String { format!( "{:}", TextRenderer::from_vdom(dom, cfg(SsrConfig::default())) @@ -110,7 +113,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option { /// /// ## Example /// ```ignore -/// static App: FC<()> = |cx, props|cx.render(rsx!(div { "hello world" })); +/// static App: Component<()> = |cx, props|cx.render(rsx!(div { "hello world" })); /// let mut vdom = VirtualDom::new(App); /// vdom.rebuild(); /// @@ -343,28 +346,28 @@ mod tests { fn to_string_works() { let mut dom = VirtualDom::new(SIMPLE_APP); dom.rebuild(); - dbg!(render_vdom(&dom, |c| c)); + dbg!(render_vdom(&dom)); } #[test] fn hydration() { let mut dom = VirtualDom::new(NESTED_APP); dom.rebuild(); - dbg!(render_vdom(&dom, |c| c.pre_render(true))); + dbg!(render_vdom_cfg(&dom, |c| c.pre_render(true))); } #[test] fn nested() { let mut dom = VirtualDom::new(NESTED_APP); dom.rebuild(); - dbg!(render_vdom(&dom, |c| c)); + dbg!(render_vdom(&dom)); } #[test] fn fragment_app() { let mut dom = VirtualDom::new(FRAGMENT_APP); dom.rebuild(); - dbg!(render_vdom(&dom, |c| c)); + dbg!(render_vdom(&dom)); } #[test] @@ -394,7 +397,7 @@ mod tests { let mut dom = VirtualDom::new(STLYE_APP); dom.rebuild(); - dbg!(render_vdom(&dom, |c| c)); + dbg!(render_vdom(&dom)); } #[test] diff --git a/packages/web/examples/async.rs b/packages/web/examples/async.rs index c6cedf9b..ae8af39b 100644 --- a/packages/web/examples/async.rs +++ b/packages/web/examples/async.rs @@ -11,11 +11,11 @@ use dioxus_web; use gloo_timers::future::TimeoutFuture; fn main() { - dioxus_web::launch(App, |c| c); + dioxus_web::launch(App); } -static App: Component<()> = |cx, props| { - let mut count = use_state(cx, || 0); +static App: Component<()> = |cx| { + let mut count = use_state(&cx, || 0); cx.push_task(|| async move { TimeoutFuture::new(100).await; diff --git a/packages/web/examples/js_bench.rs b/packages/web/examples/js_bench.rs index e81b1f04..cc7304b4 100644 --- a/packages/web/examples/js_bench.rs +++ b/packages/web/examples/js_bench.rs @@ -6,7 +6,6 @@ use dioxus_core_macro::*; use dioxus_hooks::{use_ref, use_state}; use dioxus_html as dioxus_elements; use dioxus_web; -use gloo_timers::future::TimeoutFuture; use rand::prelude::*; fn main() { @@ -73,7 +72,7 @@ fn main() { wasm_bindgen::intern(&x.to_string()); } - dioxus_web::launch(App, |c| c.rootname("main")); + dioxus_web::launch(App); } #[derive(Clone, PartialEq, Copy)] @@ -107,9 +106,9 @@ impl Label { } } -static App: Component<()> = |cx, _props| { - let mut items = use_ref(cx, || vec![]); - let mut selected = use_state(cx, || None); +static App: Component<()> = |cx| { + let mut items = use_ref(&cx, || vec![]); + let mut selected = use_state(&cx, || None); cx.render(rsx! { div { class: "container" @@ -175,10 +174,10 @@ struct ActionButtonProps<'a> { onclick: &'a dyn Fn(), } -fn ActionButton(cx: Scope, props: &ActionButtonProps) -> Element { +fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element { rsx!(cx, div { class: "col-sm-6 smallpad" - button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: move |_| (props.onclick)(), - "{props.name}" + button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}", onclick: move |_| (cx.props.onclick)(), + "{cx.props.name}" } }) } @@ -229,9 +228,9 @@ static NOUNS: &[&str] = &[ // fn Row(cx: Context, props: &RowProps) -> Element { // rsx!(cx, tr { -// td { class:"col-md-1", "{props.row_id}" } +// td { class:"col-md-1", "{cx.props.row_id}" } // td { class:"col-md-1", onclick: move |_| { /* run onselect */ } -// a { class: "lbl", {props.label.labels} } +// a { class: "lbl", {cx.props.label.labels} } // } // td { class: "col-md-1" // a { class: "remove", onclick: move |_| {/* remove */} diff --git a/packages/web/examples/simple.rs b/packages/web/examples/simple.rs index d9359195..ffb57231 100644 --- a/packages/web/examples/simple.rs +++ b/packages/web/examples/simple.rs @@ -7,16 +7,14 @@ use dioxus_core as dioxus; use dioxus_core_macro::*; use dioxus_hooks::use_state; use dioxus_html as dioxus_elements; -use dioxus_web; -use gloo_timers::future::TimeoutFuture; fn main() { wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - dioxus_web::launch(App, |c| c); + dioxus_web::launch(App); } -static App: Component<()> = |cx, props| { - let show = use_state(cx, || true); +static App: Component<()> = |cx| { + let show = use_state(&cx, || true); let inner = match *show { true => { diff --git a/packages/web/examples/suspense.rs b/packages/web/examples/suspense.rs index 615351a8..4dd1b4c3 100644 --- a/packages/web/examples/suspense.rs +++ b/packages/web/examples/suspense.rs @@ -11,10 +11,10 @@ use dioxus_html as dioxus_elements; use dioxus_web; fn main() { - dioxus_web::launch(App, |c| c); + dioxus_web::launch(App); } -static App: Component<()> = |cx, _| { +static App: Component<()> = |cx| { let doggo = cx.suspend(|| async move { #[derive(serde::Deserialize)] struct Doggo { diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index d792c372..8796ba35 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -84,12 +84,12 @@ mod ric_raf; /// dioxus_web::launch(App); /// } /// -/// static App: Component<()> = |cx, props| { +/// static App: Component<()> = |cx| { /// rsx!(cx, div {"hello world"}) /// } /// ``` pub fn launch(root_component: Component<()>) { - launch_with_props(root_component, (), |c| c); + launch_with_props(root_component, ()); } /// Launches the VirtualDOM from the specified component function and props. @@ -100,7 +100,7 @@ pub fn launch(root_component: Component<()>) { /// /// ```rust /// fn main() { -/// dioxus_web::launch_with_props(App, RootProps { name: String::from("joe") }, |c| c); +/// dioxus_web::launch_with_props(App, RootProps { name: String::from("joe") }); /// } /// /// #[derive(ParitalEq, Props)] @@ -108,8 +108,8 @@ pub fn launch(root_component: Component<()>) { /// name: String /// } /// -/// static App: Component = |cx, props| { -/// rsx!(cx, div {"hello {props.name}"}) +/// static App: Component = |cx| { +/// rsx!(cx, div {"hello {cx.props.name}"}) /// } /// ``` pub fn launch_with_props( @@ -132,7 +132,7 @@ pub fn launch_with_props( /// /// ```ignore /// fn main() { -/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") }, |c| c); +/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") }); /// wasm_bindgen_futures::spawn_local(app_fut); /// } /// ``` diff --git a/src/lib.rs b/src/lib.rs index 10a22e68..773aed0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,8 @@ //! This overview is provides a brief introduction to Dioxus. For a more in-depth guide, make sure to check out: //! - [Getting Started](https://dioxuslabs.com/getting-started) //! - [Book](https://dioxuslabs.com/book) -//! - [Reference](https://dioxuslabs.com/refernce-guide) - +//! - [Reference](https://dioxuslabs.com/reference) +//! - [Community Examples](https://github.com/DioxusLabs/community-examples) //! //! # Overview and Goals //! @@ -22,34 +22,32 @@ //! //! - Hooks for state //! - VirtualDom & diffing -//! - Concurrency & asynchronous rendering +//! - Concurrency, fibers, and asynchronous rendering //! - JSX-like templating syntax //! //! If you know React, then you know Dioxus. //! -//! Dioxus is *substantially* faster than many of the other Rust UI libraries (Yew/Percy) and is *significantly* faster -//! than React, competitve with InfernoJS and frameworks like Svelte/SolidJS. +//! Dioxus is *substantially* more performant than many of the other Rust UI libraries (Yew/Percy) and is *significantly* more performant +//! than React - roughly competitve with InfernoJS. +//! +//! Remember: Dioxus is a library for declaring interactive user interfaces - it is not a dedicated renderer. Most 1st party renderers for Dioxus currently only support web technologies. //! //! ## Brief Overview //! -//! All Dioxus apps are built by composing functions that take in a `Scope` and `Properties` and return an `Element`. A `Scope` holds -//! relevant state data for the the currently-rendered component. +//! All Dioxus apps are built by composing functions that take in a `Scope` which is generic over some `Properties` and return an `Element`. +//! A `Scope` holds relevant state data for the the currently-rendered component. +//! +//! To launch an app, we use the `launch` method for the specific renderer we want to use. In the launch function, was pass the app's `Component`. //! //! ```rust //! use dioxus::prelude::*; //! //! fn main() { -//! dioxus::desktop::launch(App); +//! dioxus::desktop::launch(app); //! } //! -//! fn App(cx: Scope, props: &()) -> Element { -//! let mut count = use_state(cx, || 0); -//! -//! cx.render(rsx!( -//! div { "Count: {count}" } -//! button { onclick: move |_| count += 1, "Increment" } -//! button { onclick: move |_| count -= 1, "Decrement" } -//! )) +//! fn app(cx: Scope<()>) -> Element { +//! cx.render(rsx!("hello world!")) //! } //! ``` //! @@ -59,7 +57,7 @@ //! For components with no explicit properties, we can use the `()` type. In Dioxus, all properties are memoized by default! //! //! ```rust -//! fn App(cx: Scope, props &()) -> Element { +//! fn App(cx: Scope<()>) -> Element { //! cx.render(rsx!( //! Header { //! title: "My App", @@ -78,11 +76,11 @@ //! color: String, //! } //! -//! fn Header(cx: Scope, props: &HeaderProps) -> Element { +//! fn Header(cx: Scope) -> Element { //! cx.render(rsx!( //! div { -//! background_color: "{props.color}" -//! h1 { "{props.title}" } +//! background_color: "{cx.props.color}" +//! h1 { "{cx.props.title}" } //! } //! )) //! } @@ -90,21 +88,65 @@ //! //! ## Hooks //! -//! While components are reusable forms of UI elements, hooks are reusable forms of logic. The details of hooks are -//! somewhat complicated. In essence, hooks let us save state between renders of our components and reuse the accompanying -//! logic across different apps. -//! -//! Hooks are simply composition of other hooks. To create our first hook we can create a simple function that takes in -//! an Scope. We can then call `use_hook` on the `Scope` to get a mutable reference to the stored value. +//! While components are reusable forms of UI elements, hooks are reusable forms of logic. All hooks start with `use_`. We can use hooks to declare state. //! //! ```rust -//! fn use_say_hello(cx: Scope) -> &mut String { +//! fn app(cx: Scope<()>) -> Element { +//! let name = use_state(&cx, || "world"); +//! +//! rsx!(cx, "hello {name}!") +//! } +//! ``` +//! +//! Hooks are sensitive to how they are used. To use hooks, you must abide by the ["rules of hooks" (borrowed from react)](https://reactjs.org/docs/hooks-rules.html): +//! - Hooks should not be called in callbacks +//! - Hooks should not be called in out of order +//! - Hooks should not be called in loops or conditionals +//! +//! In a sense, hooks let us add a field of state to our component without declaring +//! an explicit struct. However, this means we need to "load" the struct in the right +//! order. If that order is wrong, then the hook will pick the wrong state and panic. +//! +//! Most hooks you'll write are simply composition of other hooks: +//! +//! ```rust +//! fn use_username(cx: &ScopeState, id: Uuid) -> bool { +//! let users = use_context::(cx); +//! users.get(&id).is_some().map(|user| user.logged_in).ok_or(false) +//! } +//! ``` +//! +//! To create entirely new foundational hooks, we can use the `use_hook` method on ScopeState. +//! +//! ```rust +//! fn use_mut_string(cx: &ScopeState) -> &mut String { //! cx.use_hook(|_| "Hello".to_string(), |hook| hook) //! } //! ``` //! -//! If you want to extend Dioxus with some new functionality, you'll probably want to implement a new hook. +//! If you want to extend Dioxus with some new functionality, you'll probably want to implement a new hook from scratch. //! +//! ## Putting it all together +//! +//! Using components, templates, and hooks, we can build a simple app. +//! +//! ```rust +//! use dioxus::prelude::*; +//! +//! fn main() { +//! dioxus::desktop::launch(App); +//! } +//! +//! fn App(cx: Scope<()>) -> Element { +//! let mut count = use_state(&cx, || 0); +//! +//! cx.render(rsx!( +//! div { "Count: {count}" } +//! button { onclick: move |_| count += 1, "Increment" } +//! button { onclick: move |_| count -= 1, "Decrement" } +//! )) +//! } +//! ``` //! //! ## Features //! @@ -122,6 +164,23 @@ //! - Custom elements //! - Basic fine-grained reactivity (IE SolidJS/Svelte) //! - and more! +//! +//! Good luck! +//! +//! ## Inspiration, Resources, Alternatives and Credits +//! +//! Dioxus is inspired by: +//! - React: for its hooks, concurrency, suspense +//! - Dodrio: for its research in bump allocation, double buffering, and diffing architecture +//! +//! Alternatives to Dioxus include: +//! - Yew: supports function components and web, but no SSR, borrowed data, or bump allocation. Rather slow at times. +//! - Percy: supports function components, web, ssr, but lacks in state management +//! - Sycamore: supports function components, web, ssr, but closer to SolidJS than React +//! - MoonZoom/Seed: opionated in the Elm model (message, update) - no hooks +//! +//! We've put a lot of work into making Dioxus ergonomic and *familiar*. +//! Our target audience is TypeSrcipt developers looking to switch to Rust for the web - so we need to be comparabale to React. // Just a heads-up, the core functionality of dioxus rests in Dioxus-Core. This crate just wraps a bunch of utilities // together and exports their namespaces to something predicatble.