diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 94a4d0f49..65bc46198 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -104,7 +104,7 @@ pub fn component(_args: TokenStream, input: TokenStream) -> TokenStream { /// ```rust,ignore /// #[inline_props] /// fn app(bob: String) -> Element { -/// rsx!("hello, {bob}")) +/// rsx! { "hello, {bob}") } /// } /// /// // is equivalent to @@ -114,8 +114,8 @@ pub fn component(_args: TokenStream, input: TokenStream) -> TokenStream { /// bob: String, /// } /// -/// fn app(cx: Scope) -> Element { -/// rsx!("hello, {bob}")) +/// fn app(props: AppProps) -> Element { +/// rsx! { "hello, {bob}") } /// } /// ``` #[proc_macro_attribute] diff --git a/packages/dioxus-lib/README.md b/packages/dioxus-lib/README.md index bb815411a..3a733388d 100644 --- a/packages/dioxus-lib/README.md +++ b/packages/dioxus-lib/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/signals/src/comparer.rs b/packages/signals/src/comparer.rs index 013ce0584..bdcfd2180 100644 --- a/packages/signals/src/comparer.rs +++ b/packages/signals/src/comparer.rs @@ -110,7 +110,7 @@ impl>> Copy for Comparer {} /// use dioxus::prelude::*; /// use dioxus_signals::*; /// -/// fn App(cx: Scope) -> Element { +/// fn App() -> Element { /// let mut count = use_signal(cx, || 0); /// let comparer = use_comparer(cx, move || count.value()); /// @@ -129,7 +129,7 @@ impl>> Copy for Comparer {} /// } /// /// #[component] -/// fn Child(cx: Scope, active: ReadOnlySignal) -> Element { +/// fn Child(active: ReadOnlySignal) -> Element { /// if *active() { /// render! { "Active" } /// } else {