diff --git a/docs/guide/.vscode/spellright.dict b/docs/guide/.vscode/spellright.dict index 0c8e14f46..572d1f65c 100644 --- a/docs/guide/.vscode/spellright.dict +++ b/docs/guide/.vscode/spellright.dict @@ -1,2 +1,3 @@ oninput Webview +idanarye diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index 94a38800d..54d9fcd4c 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -9,9 +9,10 @@ - [Lists](elements/lists.md) - [Special Attributes](elements/special_attributes.md) - [Components](elements/components.md) - - [Component Properties](elements/propsmacro.md) - - [Reusing, Importing, and Exporting Components](elements/exporting_components.md) - - [Component Children and Attributes](elements/component_children.md) + - [Properties](elements/propsmacro.md) + - [Reusing, Importing, and Exporting](elements/exporting_components.md) + - [Children and Attributes](elements/component_children.md) + - [Composing Components](elements/composing.md) - [Adding Interactivity](interactivity/index.md) - [Hooks and Internal State](interactivity/hooks.md) - [Event handlers](interactivity/event_handlers.md) diff --git a/docs/guide/src/async/fetching.md b/docs/guide/src/async/fetching.md index 99e726c7a..b31c3d03b 100644 --- a/docs/guide/src/async/fetching.md +++ b/docs/guide/src/async/fetching.md @@ -1 +1,4 @@ # Fetching + + +This section is currently under construction! 🏗 diff --git a/docs/guide/src/async/index.md b/docs/guide/src/async/index.md index 41a8b9231..4592f5ed3 100644 --- a/docs/guide/src/async/index.md +++ b/docs/guide/src/async/index.md @@ -17,3 +17,6 @@ Writing apps that deal with Send/Sync can be frustrating at times. Under the hoo All async code in your app is polled on a `LocalSet`, so any async code we w + + +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/elements/composing.md b/docs/guide/src/elements/composing.md new file mode 100644 index 000000000..e4b00de2e --- /dev/null +++ b/docs/guide/src/elements/composing.md @@ -0,0 +1,43 @@ +# Composing Components + +So far, we've talked about declaring new components and setting up their properties. However, we haven't really talked about how components work together and how your app is updated. + +In this section, we'll talk about: + +- Sharing data between components +- How the UI is updated from input and state changes +- Forcing renders +- How renders propagate +- + + +### Rendering our posts with a PostList component + +Let's start by modeling this problem with a component and some properties. + +For this example, we're going to use the borrowed component syntax since we probably have a large list of posts that we don't want to clone every time we render the Post List. + +```rust +#[derive(Props, PartialEq)] +struct PostListProps<'a> { + posts: &'a [PostData] +} +``` +Next, we're going to define our component: + +```rust +fn App(cx: Scope) -> Element { + cx.render(rsx!{ + ul { class: "post-list", + // we can drop an iterator directly into our elements + cx.props.posts.iter().map(|post| rsx!{ + Post { + title: post.title, + age: post.age, + original_poster: post.original_poster + } + }) + } + }) +} +``` diff --git a/docs/guide/src/elements/lists.md b/docs/guide/src/elements/lists.md index 4d1d3fdf1..cdf9f6e6b 100644 --- a/docs/guide/src/elements/lists.md +++ b/docs/guide/src/elements/lists.md @@ -45,10 +45,21 @@ Finally, we can include this list in the final structure: ```rust rsx!( ul { - {name_list} + name_list } ) ``` +Or, we can include the iterator inline: +```rust +rsx!( + ul { + names.iter().map(|name| rsx!( + li { "{name}" } + )) + } +) +``` + The HTML-rendered version of this list would follow what you would expect: ```html ``` -### Rendering our posts with a PostList component - -Let's start by modeling this problem with a component and some properties. - -For this example, we're going to use the borrowed component syntax since we probably have a large list of posts that we don't want to clone every time we render the Post List. - -```rust -#[derive(Props, PartialEq)] -struct PostListProps<'a> { - posts: &'a [PostData] -} -``` -Next, we're going to define our component: - -```rust -fn App(cx: Scope) -> Element { - cx.render(rsx!{ - ul { class: "post-list", - // we can drop an iterator directly into our elements - cx.props.posts.iter().map(|post| rsx!{ - Post { - title: post.title, - age: post.age, - original_poster: post.original_poster - } - }) - } - }) -} -``` - - ## Filtering Iterators Rust's iterators are extremely powerful, especially when used for filtering tasks. When building user interfaces, you might want to display a list of items filtered by some arbitrary check. diff --git a/docs/guide/src/elements/propsmacro.md b/docs/guide/src/elements/propsmacro.md index 0821afef7..1e4e994b3 100644 --- a/docs/guide/src/elements/propsmacro.md +++ b/docs/guide/src/elements/propsmacro.md @@ -9,6 +9,151 @@ All component `properties` must implement the `Properties` trait. The `Props` ma +## Using the Props Macro + +All `properties` that your components take must implement the `Properties` trait. The simplest props you can use is simply `()` - or no value at all. `Scope` is generic over your component's props and actually defaults to `()`. + +```rust +// this scope +Scope<()> + +// is the same as this scope +Scope +``` + +If we wanted to define a component with its own props, we would create a new struct and tack on the `Props` derive macro: + +```rust +#[derive(Props)] +struct MyProps { + name: String +} +``` +This particular code will not compile - all `Props` must either a) borrow from their parent or b) implement `PartialEq`. Since our props do not borrow from their parent, they are `'static` and must implement PartialEq. + +For an owned example: +```rust +#[derive(Props, PartialEq)] +struct MyProps { + name: String +} +``` + +For a borrowed example: +```rust +#[derive(Props)] +struct MyProps<'a> { + name: &'a str +} +``` + +Then, to use these props in our component, we simply swap out the generic parameter on scope. + +For owned props, we just drop it in: + +```rust +fn Demo(cx: Scope) -> Element { + todo!() +} +``` + +However, for props that borrow data, we need to explicitly declare lifetimes. Rust does not know that our props and our component share the same lifetime, so must explicitly attach a lifetime in two places: + +```rust +fn Demo<'a>(cx: Scope<'a, MyProps<'a>>) -> Element { + todo!() +} +``` + +By putting the `'a` lifetime on Scope and our Props, we can now borrow data from our parent and pass it on to our children. + + +## Memoization + +If you're coming from React, you might be wondering how memoization fits in. For our purpose, memoization is the process in which we check if a component actually needs to be re-rendered when its props change. If a component's properties change but they wouldn't necessarily affect the output, then we don't need to actually re-render the component. + +For example, let's say we have a component that has two children: + +```rust +fn Demo(cx: Scope) -> Element { + let name = use_state(&cx, || String::from("bob")); + let age = use_state(&cx, || 21); + + cx.render(rsx!{ + Name { name: name } + Age { age: age } + }) +} +``` + +If `name` changes but `age` does not, then there is no reason to re-render our `Age` component since the contents of its props did not meaningfully change. + + +Dioxus implements memoization by default, which means you can always rely on props with `PartialEq` or no props at all to act as barriers in your app. This can be extremely useful when building larger apps where properties frequently change. By moving our state into a global state management solution, we can achieve precise, surgical re-renders, improving the performance of our app. + + +However, for components that borrow values from their parents, we cannot safely memoize them. + +For example, this component borrows `&str` - and if the parent re-renders, then the actual reference to `str` will probably be different. Since the data is borrowed, we need to pass a new version down the tree. + +```rust +#[derive(Props)] +struct MyProps<'a> { + name: &'a str +} + +fn Demo<'a>(cx: Scope<'a, MyProps<'a>>) -> Element { + todo!() +} +``` + +TLDR: +- if you see props with a lifetime or generics, it cannot be memoized +- memoization is done automatically through the `PartialEq` trait +- components with empty props can act as memoization barriers + +## Optional Fields + +Dioxus' `Props` macro is very similar to [@idanarye](https://github.com/idanarye)'s [TypedBuilder crate](https://github.com/idanarye/rust-typed-builder) and supports many of the same parameters. + +For example, you can easily create optional fields by attaching the `optional` modifier to a field. + +```rust +#[derive(Props, PartialEq)] +struct MyProps { + name: String, + + #[props(optional)] + description: Option +} + +fn Demo(cx: MyProps) -> Element { + ... +} +``` + +Then, we can completely omit the description field when calling the component: + +```rust +rsx!{ + Demo { + name: "Thing".to_string(), + // description is omitted + } +} +``` + +The `optional` modifier is a combination of two separate modifiers: `default` and `strip_option`. The full list of modifiers includes: + +- `default` - automatically add the field using its `Default` implementation +- `strip_option` - automatically wrap values at the call site in `Some` +- `optional` - combine both `default` and `strip_option` +- `into` - automatically call `into` on the value at the callsite + +For more information on how tags work, check out the [TypedBuilder](https://github.com/idanarye/rust-typed-builder) crate. However, all attributes for props in Dioxus are flattened (no need for `setter` syntax) and the `optional` field is new. + + + ## The inline_props macro diff --git a/docs/guide/src/elements/special_attributes.md b/docs/guide/src/elements/special_attributes.md index b76b19fd7..9a1122a3e 100644 --- a/docs/guide/src/elements/special_attributes.md +++ b/docs/guide/src/elements/special_attributes.md @@ -30,9 +30,7 @@ fn BlogPost(cx: Scope) -> Element { } ``` -> Note! - -This attribute is called "dangerous_inner_html" because it is DANGEROUS. If you're not careful, you can easily expose cross-site-scripting (XSS) attacks to your users. If you're handling untrusted input, make sure to escape your HTML before passing it into `dangerous_inner_html`. +> Note! This attribute is called "dangerous_inner_html" because it is DANGEROUS. If you're not careful, you can easily expose cross-site-scripting (XSS) attacks to your users. If you're handling untrusted input, make sure to escape your HTML before passing it into `dangerous_inner_html`. ## Boolean Attributes @@ -87,13 +85,13 @@ Not all attributes work like this however. Only *these specific attributes* are For any other attributes, a value of `"false"` will be sent directly to the DOM. -## `prevent_default` +## Stopping form input and navigation with `prevent_default` -Currently, preventing default on events from an event handler is not possible from Desktop/Mobile. Until this is supported, it's possible to prevent default using the `prevent_default` attribute. +Currently, calling `prevent_default` on events in EventHandlers is not possible from Desktop/Mobile. Until this is supported, it's possible to prevent default using the `prevent_default` attribute. > Note: you cannot conditionally prevent default with this approach. This is a limitation until synchronous event handling is available across the Webview boundary -To use `prevent_default`, simple use the `prevent_default` attribute and set it to the name of the event handler you want to prevent default on. We can attach this attribute multiple times for multiple attributes. +To use `prevent_default`, simply attach the `prevent_default` attribute to a given element and set it to the name of the event handler you want to prevent default on. We can attach this attribute multiple times for multiple attributes. ```rust rsx!{ @@ -107,7 +105,7 @@ rsx!{ } ``` -## `..Attributes` +## Passing attributes into children: `..Attributes` Just like Dioxus supports spreading component props into components, we also support spreading attributes into elements. This lets you pass any arbitrary attributes through components into elements. @@ -131,7 +129,6 @@ pub fn StateInput<'a>(cx: Scope<'a, InputProps<'a>>) -> Element { ## Controlled inputs and `value`, `checked`, and `selected` - In Dioxus, there is a distinction between controlled and uncontrolled inputs. Most inputs you'll use are "controlled," meaning we both drive the `value` of the input and react to the `oninput`. Controlled components: @@ -179,3 +176,15 @@ rsx!{ ``` ## Wrapping up + +We've reached just about the end of what you can do with elements without venturing into "advanced" territory. + +In this chapter, we learned: +- How to declare elements +- How to conditionally render parts of your UI +- How to render lists +- Which attributes are "special" + +There's more to elements! For further reading, check out: + +- [Custom Elements]() diff --git a/docs/guide/src/interactivity/event_handlers.md b/docs/guide/src/interactivity/event_handlers.md index ee4560b1e..8fcfe6edb 100644 --- a/docs/guide/src/interactivity/event_handlers.md +++ b/docs/guide/src/interactivity/event_handlers.md @@ -1 +1,3 @@ # Event handlers + +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/interactivity/lifecycles.md b/docs/guide/src/interactivity/lifecycles.md index 07a02f4ea..fdaa5b5e3 100644 --- a/docs/guide/src/interactivity/lifecycles.md +++ b/docs/guide/src/interactivity/lifecycles.md @@ -1 +1,3 @@ # Lifecycle, updates, and effects + +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/interactivity/user_input.md b/docs/guide/src/interactivity/user_input.md index 746443071..1e7ebbcbd 100644 --- a/docs/guide/src/interactivity/user_input.md +++ b/docs/guide/src/interactivity/user_input.md @@ -1,3 +1,6 @@ # User Input and Controlled Components Handling user input is one of the most common things your app will do, but it can be tricky + + +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/state/errorhandling.md b/docs/guide/src/state/errorhandling.md index 40436521f..767a314b0 100644 --- a/docs/guide/src/state/errorhandling.md +++ b/docs/guide/src/state/errorhandling.md @@ -9,3 +9,4 @@ fn App((cx, props): Component) -> Element { } ``` +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/state/index.md b/docs/guide/src/state/index.md index 3740dd29d..ed3a7e135 100644 --- a/docs/guide/src/state/index.md +++ b/docs/guide/src/state/index.md @@ -7,6 +7,7 @@ In this chapter, we'll cover the various ways to manage state, the appropriate t ## Terminology +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/state/liftingstate.md b/docs/guide/src/state/liftingstate.md index 3e906a0ca..fec62d2c7 100644 --- a/docs/guide/src/state/liftingstate.md +++ b/docs/guide/src/state/liftingstate.md @@ -1 +1,4 @@ # Lifting State + + +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/state/localstate.md b/docs/guide/src/state/localstate.md index 29085ac45..e54c1e45f 100644 --- a/docs/guide/src/state/localstate.md +++ b/docs/guide/src/state/localstate.md @@ -1 +1,4 @@ # Local State + + +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/state/sharedstate.md b/docs/guide/src/state/sharedstate.md index ec1ede1d1..148b6eb66 100644 --- a/docs/guide/src/state/sharedstate.md +++ b/docs/guide/src/state/sharedstate.md @@ -1 +1,7 @@ # Global State + + +cx.provide_context() +cx.consume_context() + +> This section is currently under construction! 🏗 diff --git a/docs/guide/src/tutorial/components.md b/docs/guide/src/tutorial/components.md index bfbb760b3..82bae6cd7 100644 --- a/docs/guide/src/tutorial/components.md +++ b/docs/guide/src/tutorial/components.md @@ -1 +1,3 @@ # Defining Components + +This section is currently under construction! 🏗 diff --git a/docs/guide/src/tutorial/state.md b/docs/guide/src/tutorial/state.md index 35df690d6..6026c7546 100644 --- a/docs/guide/src/tutorial/state.md +++ b/docs/guide/src/tutorial/state.md @@ -1 +1,3 @@ # Defining State + +This section is currently under construction! 🏗 diff --git a/docs/guide/src/tutorial/structure.md b/docs/guide/src/tutorial/structure.md index 1cbb47579..eaf8afe43 100644 --- a/docs/guide/src/tutorial/structure.md +++ b/docs/guide/src/tutorial/structure.md @@ -1 +1,4 @@ # Structuring our app + + +This section is currently under construction! 🏗 diff --git a/docs/guide/src/tutorial/styling.md b/docs/guide/src/tutorial/styling.md index 3e90761b5..b25db14d2 100644 --- a/docs/guide/src/tutorial/styling.md +++ b/docs/guide/src/tutorial/styling.md @@ -1 +1,4 @@ # Styling + + +This section is currently under construction! 🏗