From 4de16c4779648e591b3869b5df31271ae603c812 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 15 Dec 2021 15:56:53 -0500 Subject: [PATCH] docs: update local examples and docs to support new syntaxes --- .vscode/settings.json | 6 +- README.md | 19 +- docs/guide/src/README.md | 4 +- docs/guide/src/concepts/00-index.md | 2 +- docs/guide/src/concepts/11-arena-memo.md | 6 +- docs/guide/src/concepts/12-signals.md | 10 +- docs/guide/src/concepts/components.md | 4 +- .../src/concepts/conditional_rendering.md | 2 +- .../src/concepts/exporting_components.md | 6 +- docs/guide/src/concepts/interactivity.md | 6 +- docs/guide/src/hello_world.md | 6 +- examples/README.md | 10 +- examples/async.rs | 8 +- examples/borrowed.rs | 12 +- examples/calculator.rs | 18 +- examples/core/alternative.rs | 2 +- examples/core/async.rs | 2 +- examples/core/contextapi.rs | 2 +- examples/core/jsframework.rs | 4 +- examples/core/syntax.rs | 10 +- examples/core/syntax2.rs | 2 +- examples/core/vdom_usage.rs | 2 +- examples/core_reference/antipatterns.rs | 24 +- examples/core_reference/basics.rs | 6 +- examples/core_reference/children.rs | 4 +- .../core_reference/conditional_rendering.rs | 14 +- examples/core_reference/controlled_inputs.rs | 10 +- examples/core_reference/custom_elements.rs | 2 +- examples/core_reference/empty.rs | 2 +- examples/core_reference/errorhandling.rs | 12 +- examples/core_reference/fragments.rs | 8 +- examples/core_reference/global_css.rs | 2 +- examples/core_reference/inline_styles.rs | 4 +- examples/core_reference/iterators.rs | 4 +- examples/core_reference/listener.rs | 10 +- examples/core_reference/memo.rs | 14 +- examples/core_reference/noderefs.rs | 2 +- examples/core_reference/signals.rs | 2 +- examples/core_reference/spreadpattern.rs | 4 +- examples/core_reference/statemanagement.rs | 2 +- examples/core_reference/suspense.rs | 2 +- examples/core_reference/task.rs | 6 +- examples/core_reference/testing.rs | 2 +- examples/core_reference/tostring.rs | 8 +- examples/coroutine.rs | 8 +- examples/crm.rs | 14 +- examples/desktop/crm.rs | 14 +- examples/desktop/demo.rs | 4 +- examples/desktop/todomvc.rs | 12 +- examples/file_explorer.rs | 22 +- examples/framework_benchmark.rs | 20 +- examples/hello_world.rs | 2 +- examples/hydration.rs | 4 +- examples/pattern_model.rs | 12 +- examples/pattern_reducer.rs | 6 +- examples/readme.rs | 6 +- examples/router.rs | 2 +- examples/rsx_usage.rs | 4 +- examples/ssr.rs | 8 +- examples/ssr/basic.rs | 2 +- examples/ssr/tide.rs | 6 +- examples/ssr/tofile.rs | 12 +- examples/tailwind.rs | 12 +- examples/tasks.rs | 6 +- examples/todomvc.rs | 16 +- examples/weather_app.rs | 6 +- examples/web/async_web.rs | 6 +- examples/web/basic.rs | 12 +- examples/web/blah.rs | 6 +- examples/web/btns.rs | 10 +- examples/web/crm2.rs | 14 +- examples/web/demo.rs | 14 +- examples/web_tick.rs | 6 +- examples/webview_web.rs | 6 +- notes/Parity.md | 2 +- notes/SOLVEDPROBLEMS.md | 10 +- packages/core-macro/src/lib.rs | 2 +- packages/core-macro/src/rsx/ambiguous.rs | 6 +- packages/core-macro/src/rsx/component.rs | 9 +- packages/core/Cargo.toml | 4 +- packages/core/README.md | 57 +-- packages/core/architecture.md | 2 + packages/core/benches/jsframework.rs | 8 +- packages/core/examples/borrowed.rs | 18 - packages/core/examples/hooks.rs | 8 +- packages/core/flamegraph.svg | 412 ------------------ packages/core/src/component.rs | 2 +- packages/core/src/diff.rs | 13 +- packages/core/src/nodes.rs | 4 +- packages/core/src/scope.rs | 10 +- packages/core/src/scopearena.rs | 53 +-- packages/core/src/virtual_dom.rs | 8 +- packages/core/tests/lifecycle.rs | 1 - packages/desktop/README.md | 18 +- packages/desktop/examples/async.rs | 6 +- packages/desktop/src/desktop_context.rs | 2 +- packages/desktop/src/lib.rs | 8 +- packages/hooks/Cargo.toml | 5 +- packages/hooks/README.md | 4 +- packages/hooks/src/lib.rs | 3 - packages/hooks/src/usecollection.rs | 4 +- packages/hooks/src/usecoroutine.rs | 2 +- packages/hooks/src/usemodel.rs | 2 +- packages/hooks/src/usestate.rs | 2 +- packages/hooks/src/usestate2.rs | 227 ---------- packages/hooks/src/usetask.rs | 0 packages/liveview/README.md | 2 +- packages/mobile/src/lib.rs | 2 +- packages/router/README.md | 4 +- packages/router/examples/simple.rs | 6 +- packages/ssr/README.md | 8 +- packages/ssr/src/lib.rs | 17 +- packages/web/examples/async.rs | 6 +- packages/web/examples/js_bench.rs | 19 +- packages/web/examples/simple.rs | 8 +- packages/web/examples/suspense.rs | 4 +- packages/web/src/lib.rs | 12 +- src/lib.rs | 115 +++-- 118 files changed, 520 insertions(+), 1154 deletions(-) delete mode 100644 packages/core/flamegraph.svg delete mode 100644 packages/hooks/src/usestate2.rs delete mode 100644 packages/hooks/src/usetask.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index b3bde867c..ac2195ccd 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 a5f0e8448..fddea2882 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 faf58e309..419828620 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 715c3d69b..cc432cf9a 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 28eccda24..12f99a1d6 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 5000faebc..3737a4885 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 ef750f87a..9eb67ab39 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 e35fce36d..ef62b8701 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 9b5a65ca1..6139a97e6 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 bd8ff14aa..63cf6741c 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 2520c286a..2b0cebf87 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 06a73db07..2c05466a2 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 7143a9af6..16f28809e 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 35ec2cfba..cf497bbc4 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 442ddb7f8..6a83bfa63 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 6c20e2adf..a009a88cb 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 a21f82652..2919651d2 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 6c90a60c7..d538d49ae 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 207f6e84e..15c25ec8d 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 9a21bc7e6..2339af8ad 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 54c0bedaa..0b3a9e049 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 cbe3c324f..844174062 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 735a57104..50fecc4f5 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 6a3807bb2..11c3a59c3 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 bdd3108d0..48c940b60 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 6a5fe9a64..d73f562e0 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 bdabe83d2..2c97ec1ff 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 053b3aea5..63024213a 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 28914bd6b..aebf7e71f 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 e598726e5..cc21a62f9 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 88c9dcaea..0532abd27 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 5e7c75a8c..6b7de70f0 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 e3a45154c..c60364b75 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 f8feae8a8..55d725a21 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 63813e423..891ee3743 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 40aa54191..86831842b 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 8d22ee5b4..c520abdca 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 2e69d8e66..06fb6ab1b 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 58a813cb5..c197f87e6 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 2e69d8e66..06fb6ab1b 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 e3bbd2574..bbe536d3e 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 a5e877bc6..7fad94381 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 587ce19cc..ec10acaa3 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 a04a165da..09f8d95d1 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 61bed91c3..e112c2995 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 f146e0856..2ae7d3ee0 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 8cc5b2988..f216f884f 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 77a927e99..d327b7f32 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 d8edf40e4..2674d9dee 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 74ea6a7d9..98b729896 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 44fe202e1..fada80ad4 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 e6c874ba5..f788f9791 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 22a81ffe7..8189f7cdd 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 d0a9e7290..f59a548cb 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 42c1976f9..7441516a3 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 afff68f87..aa4cd9a8a 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 66b86f56d..be055fe9e 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 e27d4c828..9b6283d92 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 e23251ada..542417c00 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 c89c425e3..89b976d42 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 d86fbdf55..d7255b91d 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 e3091580a..8440074e8 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 555d13d5d..70a0448fd 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 699ae1274..3b14faff1 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 395b86696..b73d4ef49 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 20fde8a28..0ba2ffa0f 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 a64ea0598..a1a0f8bb1 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 4c4d52ce5..ff70f8a1b 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 d21cd5fd0..5553ceede 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 179074ee7..a3c96ad7a 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 4dfe9db34..4f0eff48f 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 603b84de5..6c21aa48d 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 3a24ce523..098bfc1f3 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 55f9f4309..a31096be7 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 5cc0851c9..fa51fff58 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 3c42094ee..baf88701b 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 6e754a163..6d36280cb 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 71b3a2c4c..16cb2c4e5 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 23ff3b537..9c51cb748 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 deca59cde..1788427d7 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 ce2602003..71ee252cb 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 a3f468c25..f852fda3c 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 4f5f762d1..34884983a 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 71a4f2fb9..784f48740 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 d4d217fde..969d9f3f2 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 0699a198c..000000000 --- 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 f8d3c9727..75a3d4e17 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 35f7470ba..5783a696e 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 9514f83f9..1f8b8b1c7 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 5d07b3920..6289811e0 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 9716a3ae1..9afb80866 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 63d64efc7..23d7570cc 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 d3976fdea..2da5b5bd6 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 0c1663488..18225ade7 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 b86b66df4..2626355e4 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 b6e8ac4c7..9a0607218 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 df97b49a8..59654aeb9 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 2327308b4..93133865e 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 39ab816b5..11e008c44 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 f6a5227bc..ef5b699b6 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 d0b3b71e7..47005e57b 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 f04fb4f12..f593c2b77 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 47981af0f..642081cf8 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 0c5bb708a..1c4fe2d04 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 6cffc8e95..000000000 --- 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 e69de29bb..000000000 diff --git a/packages/liveview/README.md b/packages/liveview/README.md index 1e29ac75a..4a05d9f10 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 6d7f27114..e11765dda 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 598c4f6a9..05181ec9b 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 6c604b88a..5f8105223 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 4d47b426d..02f6e09d8 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 80de92680..a29a7db1c 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 c6cedf9be..ae8af39bd 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 e81b1f04b..cc7304b40 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 d93591952..ffb572314 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 615351a81..4dd1b4c33 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 d792c3725..8796ba35e 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 10a22e685..773aed0cb 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.