diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs index 5e020b251..9007a60b5 100644 --- a/packages/core/src/global_context.rs +++ b/packages/core/src/global_context.rs @@ -196,24 +196,19 @@ pub fn schedule_update_any() -> Arc { /// /// fn child_component() -> Element { /// let original_scroll_position = use_signal(|| 0.0); -/// use_effect((), move |_| { -/// to_owned![original_scroll_position]; -/// async move { -/// let window = web_sys::window().unwrap(); -/// let document = window.document().unwrap(); -/// let element = document.get_element_by_id("my_element").unwrap(); -/// element.scroll_into_view(); -/// original_scroll_position.set(window.scroll_y().unwrap()); -/// } +/// +/// use_effect(move |_| async move { +/// let window = web_sys::window().unwrap(); +/// let document = window.document().unwrap(); +/// let element = document.get_element_by_id("my_element").unwrap(); +/// element.scroll_into_view(); +/// original_scroll_position.set(window.scroll_y().unwrap()); /// }); /// -/// use_drop({ -/// to_owned![original_scroll_position]; +/// use_drop(move || { /// /// restore scroll to the top of the page -/// move || { -/// let window = web_sys::window().unwrap(); -/// window.scroll_with_x_and_y(*original_scroll_position.current(), 0.0); -/// } +/// let window = web_sys::window().unwrap(); +/// window.scroll_with_x_and_y(*original_scroll_position.current(), 0.0); /// }); /// /// rsx!{ diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index eb4b5b6ff..9a0b9fec2 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -46,33 +46,6 @@ pub(crate) mod innerlude { pub type Element = Option; /// A [`Component`] is a function that takes [`Properties`] and returns an [`Element`]. - /// - /// Components can be used in other components with two syntax options: - /// - lowercase as a function call with named arguments (rust style) - /// - uppercase as an element (react style) - /// - /// ## Rust-Style - /// - /// ```rust, ignore - /// fn example(cx: Props) -> Element { - /// // ... - /// } - /// - /// rsx!( - /// example() - /// ) - /// ``` - /// - /// ## React-Style - /// ```rust, ignore - /// fn Example(cx: Props) -> Element { - /// // ... - /// } - /// - /// rsx!( - /// Example {} - /// ) - /// ``` pub type Component

= fn(P) -> Element; } diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 4f150e3a4..570318633 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -20,11 +20,7 @@ pub type TemplateId = &'static str; /// The actual state of the component's most recent computation /// -/// Because Dioxus accepts components in the form of `async fn() -> Result`, we need to support both -/// sync and async versions. -/// -/// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor -/// you might need to handle the case where there's no node immediately ready. +/// If the component returned early (e.g. `return None`), this will be Aborted(None) pub enum RenderReturn { /// A currently-available element Ready(VNode), diff --git a/packages/dioxus-lib/README.md b/packages/dioxus-lib/README.md index 3a733388d..e8f51e17d 100644 --- a/packages/dioxus-lib/README.md +++ b/packages/dioxus-lib/README.md @@ -5,6 +5,8 @@

+> This crate implements dioxus-lib which is a renderer-free version of Dioxus. This crate is intended to be used by library authors who need a stable core verison of dioxus withuot pulling in renderer-related dependencies on accident. + # Resources This overview provides a brief introduction to Dioxus. For a more in-depth guide, make sure to check out: diff --git a/packages/dioxus/README.md b/packages/dioxus/README.md index bb815411a..3a733388d 100644 --- a/packages/dioxus/README.md +++ b/packages/dioxus/README.md @@ -34,11 +34,11 @@ Remember: Dioxus is a library for declaring interactive user interfaces—it is ## Brief Overview -All Dioxus apps are built by composing functions that return an `Element`. +All Dioxus apps are built by composing functions that return an `Element`. To launch an app, we use the `launch` method and use features in ``Cargo.toml`` to specify which renderer we want to use. In the launch function, we pass the app's root `Component`. -```rust, ignore +```rust use dioxus::prelude::*; fn main() { @@ -49,7 +49,7 @@ fn main() { // It's not required, but highly recommended. For example, UpperCamelCase components will not generate a warning. #[component] fn App() -> Element { - rsx!("hello world!") + rsx! { "hello world!" } } ``` @@ -67,25 +67,25 @@ children. ```rust, ignore let value = "123"; -rsx!( +rsx! { div { class: "my-class {value}", // <--- attribute onclick: move |_| info!("clicked!"), // <--- listener h1 { "hello world" }, // <--- child } -) +} ``` -The `rsx!` macro accepts attributes in "struct form" and will parse the rest -of the body as child elements and rust expressions. Any rust expression that -implements `IntoIterator` will be parsed as a child. +The `rsx!` macro accepts attributes in "struct form". Any rust expression contained within curly braces that implements `IntoIterator` will be parsed as a child. We make two exceptions: both `for` loops and `if` statements are parsed where their body is parsed as a child. ```rust, ignore -rsx!( +rsx! { div { - (0..10).map(|_| rsx!(span { "hello world" })) + for _ in 0..10 { + span { "hello world" } + } } -) +} ``` The `rsx!` macro is what generates the `Element` that our components return. @@ -93,7 +93,7 @@ The `rsx!` macro is what generates the `Element` that our components return. ```rust, ignore #[component] fn Example() -> Element { - rsx!("hello world") + rsx!{ "hello world" } } ``` @@ -104,20 +104,14 @@ elements: #[component] fn App() -> Element { let name = "dave"; - rsx!( + rsx! { h1 { "Hello, {name}!" } - div { - class: "my-class", - id: "my-id", - - (0..5).map(|i| rsx!( - div { key: "{i}" - "FizzBuzz: {i}" - } - )) - + div { class: "my-class", id: "my-id", + for i in 0..5 { + div { "FizzBuzz: {i}" } + } } - ) + } } ``` @@ -125,19 +119,19 @@ fn App() -> Element { We can compose these function components to build a complex app. Each new component we design must take some Properties. For components with no explicit -properties, we can use the `()` type or simply omit the type altogether. +properties we can omit the type altogether. -In Dioxus, all properties are memoized by default! +In Dioxus, all properties are memoized by default, and this implement both Clone and PartialEq. For props you can't clone, simply wrap the fields in a ReadOnlySignal and Dioxus will handle the wrapping for you. ```rust, ignore #[component] fn App() -> Element { - rsx!( + rsx! { Header { title: "My App", color: "red", } - ) + } } ``` @@ -154,12 +148,12 @@ struct HeaderProps { #[component] fn Header(props: HeaderProps) -> Element { - rsx!( + rsx! { div { background_color: "{props.color}" h1 { "{props.title}" } } - ) + } } ``` @@ -169,35 +163,12 @@ struct from function arguments: ```rust, ignore #[component] fn Header(title: String, color: String) -> Element { - rsx!( + rsx! { div { background_color: "{color}" h1 { "{title}" } } - ) -} -``` - -Components may also borrow data from their parent component. We just need to -attach some lifetimes to the props struct. - -> Note: we don't need to derive `PartialEq` for borrowed props since they cannot be memoized. - -```rust, ignore -#[derive(Props)] -struct HeaderProps<'a> { - title: &'a str, - color: &'a str, -} - -#[component] -fn Header<'a>(props: HeaderProps<'a>) -> Element { - rsx!( - div { - background_color: "{props.color}" - h1 { "{props.title}" } - } - ) + } } ``` @@ -205,23 +176,11 @@ Components that begin with an uppercase letter may be called with the traditional (for React) curly-brace syntax like so: ```rust, ignore -rsx!( +rsx! { Header { title: "My App" } -) +} ``` -Alternatively, if your components begin with a lowercase letter, you can use -the function call syntax: - -```rust, ignore -rsx!( - header( title: "My App" ) -) -``` - -However, the convention is to use UpperCamelCase. The `#[component]` attribute will enforce this, -but you can turn it off if you wish. - ## Hooks While components are reusable forms of UI elements, hooks are reusable forms @@ -236,7 +195,7 @@ use hooks to define the state and modify it from within listeners. fn App() -> Element { let name = use_signal(|| "world"); - rsx!("hello {name}!") + rsx! { "hello {name}!" } } ``` @@ -263,7 +222,7 @@ fn use_username(d: Uuid) -> bool { To create entirely new foundational hooks, we can use the `use_hook` method. ```rust, ignore -fn use_mut_string() -> &mut String { +fn use_mut_string() -> String { use_hook(|_| "Hello".to_string()) } ``` @@ -283,12 +242,12 @@ fn main() { #[component] fn App() -> Element { - let count = use_signal(|| 0); + let mut count = use_signal(|| 0); rsx!( div { "Count: {count}" } - button { onclick: move |_| count.set(count + 1), "Increment" } - button { onclick: move |_| count.set(count - 1), "Decrement" } + button { onclick: move |_| count += 1, "Increment" } + button { onclick: move |_| count -= 1, "Decrement" } ) } ``` @@ -311,21 +270,4 @@ Beyond this overview, Dioxus supports: - 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 state management -- Sycamore: supports function components, web, ssr, but is closer to SolidJS than React -- MoonZoom/Seed: opinionated frameworks based on the Elm model (message, update)—no hooks - -We've put a lot of work into making Dioxus ergonomic and _familiar_. -Our target audience is TypeScript developers looking to switch to Rust for the web—so we need to be comparable to React. +Build cool things ✌️ diff --git a/packages/fullstack/examples/axum-auth/src/main.rs b/packages/fullstack/examples/axum-auth/src/main.rs index 7410da416..956869bbf 100644 --- a/packages/fullstack/examples/axum-auth/src/main.rs +++ b/packages/fullstack/examples/axum-auth/src/main.rs @@ -76,13 +76,12 @@ fn main() { } // fn app() -> Element { - let user_name = use_signal(|| "?".to_string()); - let permissions = use_signal(|| "?".to_string()); + let mut user_name = use_signal(|| "?".to_string()); + let mut permissions = use_signal(|| "?".to_string()); rsx! { div { - button { - onclick: move |_| { + button { onclick: move |_| { async move { login().await.unwrap(); } @@ -92,12 +91,9 @@ fn app() -> Element { } div { button { - onclick: move |_| { - to_owned![user_name]; - async move { - if let Ok(data) = get_user_name().await { - user_name.set(data); - } + onclick: move |_| async move { + if let Ok(data) = get_user_name().await { + user_name.set(data); } }, "Get User Name" @@ -106,12 +102,9 @@ fn app() -> Element { } div { button { - onclick: move |_| { - to_owned![permissions]; - async move { - if let Ok(data) = get_permissions().await { - permissions.set(data); - } + onclick: move |_| async move { + if let Ok(data) = get_permissions().await { + permissions.set(data); } }, "Get Permissions" diff --git a/packages/fullstack/examples/axum-desktop/src/lib.rs b/packages/fullstack/examples/axum-desktop/src/lib.rs index b0ea219a7..8ca97331e 100644 --- a/packages/fullstack/examples/axum-desktop/src/lib.rs +++ b/packages/fullstack/examples/axum-desktop/src/lib.rs @@ -3,21 +3,18 @@ use dioxus::prelude::*; pub fn app() -> Element { let mut count = use_signal(|| 0); - let text = use_signal(|| "...".to_string()); + let mut text = use_signal(|| "...".to_string()); rsx! { h1 { "High-Five counter: {count}" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } button { - onclick: move |_| { - to_owned![text]; - async move { - if let Ok(data) = get_server_data().await { - println!("Client received: {}", data); - text.set(data.clone()); - post_server_data(data).await.unwrap(); - } + onclick: move |_| async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); } }, "Run a server function" diff --git a/packages/fullstack/examples/axum-hello-world/src/main.rs b/packages/fullstack/examples/axum-hello-world/src/main.rs index b7912facf..bde19e467 100644 --- a/packages/fullstack/examples/axum-hello-world/src/main.rs +++ b/packages/fullstack/examples/axum-hello-world/src/main.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; fn app() -> Element { let mut count = use_signal(|| 0); - let text = use_signal(|| "...".to_string()); + let mut text = use_signal(|| "...".to_string()); let server_future = use_server_future(get_server_data)?; rsx! { @@ -18,14 +18,11 @@ fn app() -> Element { button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } button { - onclick: move |_| { - to_owned![text]; - async move { - if let Ok(data) = get_server_data().await { - println!("Client received: {}", data); - text.set(data.clone()); - post_server_data(data).await.unwrap(); - } + onclick: move |_| async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); } }, "Run a server function!" diff --git a/packages/fullstack/examples/axum-router/src/main.rs b/packages/fullstack/examples/axum-router/src/main.rs index 6aed7429d..95bc3b9b5 100644 --- a/packages/fullstack/examples/axum-router/src/main.rs +++ b/packages/fullstack/examples/axum-router/src/main.rs @@ -15,9 +15,7 @@ fn main() { } fn app() -> Element { - rsx! { - Router:: {} - } + rsx! { Router:: {} } } #[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)] @@ -50,28 +48,20 @@ fn Blog(id: i32) -> Element { #[component] fn Home() -> Element { let mut count = use_signal(|| 0); - let text = use_signal(|| "...".to_string()); + let mut text = use_signal(|| "...".to_string()); rsx! { - Link { - to: Route::Blog { - id: count() - }, - "Go to blog" - } + Link { to: Route::Blog { id: count() }, "Go to blog" } div { h1 { "High-Five counter: {count}" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } button { - onclick: move |_| { - to_owned![text]; - async move { - if let Ok(data) = get_server_data().await { - println!("Client received: {}", data); - text.set(data.clone()); - post_server_data(data).await.unwrap(); - } + onclick: move |_| async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); } }, "Run server function!" diff --git a/packages/fullstack/src/axum_adapter.rs b/packages/fullstack/src/axum_adapter.rs index cf4a2bdf5..d02dc9b3d 100644 --- a/packages/fullstack/src/axum_adapter.rs +++ b/packages/fullstack/src/axum_adapter.rs @@ -30,16 +30,13 @@ //! } //! //! fn app() -> Element { -//! let text = use_signal(|| "...".to_string()); +//! let mut text = use_signal(|| "...".to_string()); //! //! rsx! { //! button { -//! onclick: move |_| { -//! to_owned![text]; -//! async move { -//! if let Ok(data) = get_server_data().await { -//! text.set(data); -//! } +//! onclick: move |_| async move { +//! if let Ok(data) = get_server_data().await { +//! text.set(data); //! } //! }, //! "Run a server function" diff --git a/packages/hooks/README.md b/packages/hooks/README.md index 3e7cbc68c..5dcc5c5fa 100644 --- a/packages/hooks/README.md +++ b/packages/hooks/README.md @@ -32,10 +32,6 @@ Unlike React, none of these hooks are foundational since they all build off the primitive `use_hook`. -This crate also provides a helpful macro to get around some Rust lifetime management issues in async: - -- `to_owned![]` - ## Contributing - Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues). diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index 3af9dddda..fd5b7ae45 100644 --- a/packages/hooks/src/lib.rs +++ b/packages/hooks/src/lib.rs @@ -3,7 +3,7 @@ #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] #[macro_export] -/// A helper macro for using hooks and properties in async environments. +/// A helper macro for cloning multiple values at once /// /// # Usage ///