From bdf234d72804d984e286e4c559e1be5ef7455785 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 9 Jan 2022 22:28:07 -0500 Subject: [PATCH 1/9] docs: improve components and elements --- docs/guide/.vscode/spellright.dict | 2 + docs/guide/src/SUMMARY.md | 9 +- docs/guide/src/elements/components.md | 36 ---- docs/guide/src/elements/lists.md | 21 +- docs/guide/src/elements/propsmacro.md | 48 ++++- docs/guide/src/elements/special_attributes.md | 181 ++++++++++++++++++ docs/guide/src/elements/vnodes.md | 6 +- docs/guide/src/interactivity/user_input.md | 2 + docs/guide/src/state/index.md | 19 +- 9 files changed, 267 insertions(+), 57 deletions(-) create mode 100644 docs/guide/.vscode/spellright.dict create mode 100644 docs/guide/src/elements/special_attributes.md diff --git a/docs/guide/.vscode/spellright.dict b/docs/guide/.vscode/spellright.dict new file mode 100644 index 000000000..0c8e14f46 --- /dev/null +++ b/docs/guide/.vscode/spellright.dict @@ -0,0 +1,2 @@ +oninput +Webview diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index 2e77d4ab8..94a38800d 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -5,12 +5,13 @@ - [Hello, World!](hello_world.md) - [Describing the UI](elements/index.md) - [Intro to Elements](elements/vnodes.md) - - [Intro to Components](elements/components.md) - - [The Props Macro](elements/propsmacro.md) - - [Reusing, Importing, and Exporting Components](elements/exporting_components.md) - - [Passing children and attributes](elements/component_children.md) - [Conditional Rendering](elements/conditional_rendering.md) - [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) - [Adding Interactivity](interactivity/index.md) - [Hooks and Internal State](interactivity/hooks.md) - [Event handlers](interactivity/event_handlers.md) diff --git a/docs/guide/src/elements/components.md b/docs/guide/src/elements/components.md index 8567e9270..bd22bb88c 100644 --- a/docs/guide/src/elements/components.md +++ b/docs/guide/src/elements/components.md @@ -156,42 +156,6 @@ For users of React: Dioxus knows *not* to memoize components that borrow propert This means that during the render process, a newer version of `TitleCardProps` will never be compared with a previous version, saving some clock cycles. -## The inline_props macro - -Yes - *another* macro! However, this one is entirely optional. - -For internal components, we provide the `inline_props` macro, which will let you embed your `Props` definition right into the function arguments of your component. - -Our title card above would be transformed from: - -```rust -#[derive(Props, PartialEq)] -struct TitleCardProps { - title: String, -} - -fn TitleCard(cx: Scope) -> Element { - cx.render(rsx!{ - h1 { "{cx.props.title}" } - }) -} -``` - -to: - -```rust -#[inline_props] -fn TitleCard(cx: Scope, title: String) -> Element { - cx.render(rsx!{ - h1 { "{title}" } - }) -} -``` - -Again, this macro is optional and should not be used by library authors since you have less fine-grained control over documentation and optionality. - -However, it's great for quickly throwing together an app without dealing with *any* extra boilerplate. - ## The `Scope` object Though very similar to React, Dioxus is different in a few ways. Most notably, React components will not have a `Scope` parameter in the component declaration. diff --git a/docs/guide/src/elements/lists.md b/docs/guide/src/elements/lists.md index 32dbfd833..4d1d3fdf1 100644 --- a/docs/guide/src/elements/lists.md +++ b/docs/guide/src/elements/lists.md @@ -75,19 +75,16 @@ Next, we're going to define our component: ```rust fn App(cx: Scope) -> Element { - // First, we create a new iterator by mapping the post array - let posts = cx.props.posts.iter().map(|post| rsx!{ - Post { - title: post.title, - age: post.age, - original_poster: post.original_poster - } - }); - - // Finally, we render the post list inside of a container cx.render(rsx!{ - ul { class: "post-list" - {posts} + 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/propsmacro.md b/docs/guide/src/elements/propsmacro.md index 7426a73bf..0821afef7 100644 --- a/docs/guide/src/elements/propsmacro.md +++ b/docs/guide/src/elements/propsmacro.md @@ -1 +1,47 @@ -# The Props Macro +# Component Properties + +All component `properties` must implement the `Properties` trait. The `Props` macro automatically derives this trait but adds some additional functionality. In this section, we'll learn about: + +- Using the props macro +- Memoization through PartialEq +- Optional fields on props +- The inline_props macro + + + + +## The inline_props macro + +Yes - *another* macro! However, this one is entirely optional. + +For internal components, we provide the `inline_props` macro, which will let you embed your `Props` definition right into the function arguments of your component. + +Our title card above would be transformed from: + +```rust +#[derive(Props, PartialEq)] +struct TitleCardProps { + title: String, +} + +fn TitleCard(cx: Scope) -> Element { + cx.render(rsx!{ + h1 { "{cx.props.title}" } + }) +} +``` + +to: + +```rust +#[inline_props] +fn TitleCard(cx: Scope, title: String) -> Element { + cx.render(rsx!{ + h1 { "{title}" } + }) +} +``` + +Again, this macro is optional and should not be used by library authors since you have less fine-grained control over documentation and optionality. + +However, it's great for quickly throwing together an app without dealing with *any* extra boilerplate. diff --git a/docs/guide/src/elements/special_attributes.md b/docs/guide/src/elements/special_attributes.md new file mode 100644 index 000000000..b76b19fd7 --- /dev/null +++ b/docs/guide/src/elements/special_attributes.md @@ -0,0 +1,181 @@ +# Special Attributes + +Dioxus tries its hardest to stay close to React, but there are some divergences and "special behavior" that you should review before moving on. + +In this section, we'll cover special attributes built into Dioxus: + +- `dangerous_inner_html` +- Boolean attributes +- `prevent_default` +- `..Attributes` +- event handlers as string attributes +- `value`, `checked`, and `selected` + +## The HTML escape hatch: `dangerous_inner_html` + +One thing you might've missed from React is the ability to render raw HTML directly to the DOM. If you're working with pre-rendered assets, output from templates, or output from a JS library, then you might want to pass HTML directly instead of going through Dioxus. In these instances, reach for `dangerous_inner_html`. + +For example, shipping a markdown-to-Dioxus converter might significantly bloat your final application size. Instead, you'll want to pre-render your markdown to HTML and then include the HTML directly in your output. We use this approach for the `http://dioxuslabs.com` site: + + +```rust +fn BlogPost(cx: Scope) -> Element { + let contents = include_str!("../post.html"); + cx.render(rsx!{ + div { + class: "markdown", + dangerous_inner_html: "{contents}", + } + }) +} +``` + +> 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 + +Most attributes, when rendered, will be rendered exactly as the input you provided. However, some attributes are considered "boolean" attributes and just their presence determines whether or not they affect the output. For these attributes, a provided value of `"false"` will cause them to be removed from the target element. + +So the input of: + +```rust +rsx!{ + div { + hidden: "false", + "hello" + } +} +``` +would actually render an output of +```html +
hello
+``` + +Notice how `hidden` is not present in the final output? + +Not all attributes work like this however. Only *these specific attributes* are whitelisted to have this behavior: + +- `allowfullscreen` +- `allowpaymentrequest` +- `async` +- `autofocus` +- `autoplay` +- `checked` +- `controls` +- `default` +- `defer` +- `disabled` +- `formnovalidate` +- `hidden` +- `ismap` +- `itemscope` +- `loop` +- `multiple` +- `muted` +- `nomodule` +- `novalidate` +- `open` +- `playsinline` +- `readonly` +- `required` +- `reversed` +- `selected` +- `truespeed` + +For any other attributes, a value of `"false"` will be sent directly to the DOM. + +## `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. + +> 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. + +```rust +rsx!{ + input { + oninput: move |_| {}, + prevent_default: "oninput", + + onclick: move |_| {}, + prevent_default: "onclick", + } +} +``` + +## `..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. + + +```rust +#[derive(Props)] +pub struct InputProps<'a> { + pub children: Element<'a>, + pub attributes: Attribute<'a> +} + +pub fn StateInput<'a>(cx: Scope<'a, InputProps<'a>>) -> Element { + cx.render(rsx! ( + input { + ..cx.props.attributes, + &cx.props.children, + } + )) +} +``` + +## 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: +```rust +let value = use_state(&cx, || String::from("hello world")); + +rsx! { + input { + oninput: move |evt| value.set(evt.value.clone()), + value: "{value}", + } +} +``` + +With uncontrolled inputs, we won't actually drive the value from the component. This has its advantages when we don't want to re-render the component when the user inputs a value. We could either select the element directly - something Dioxus doesn't support across platforms - or we could handle `oninput` and modify a value without causing an update: + +```rust +let value = use_ref(&cx, || String::from("hello world")); + +rsx! { + input { + oninput: move |evt| *value.write_silent() = evt.value.clone(), + // no "value" is driven + } +} +``` + +## Strings for handlers like `onclick` + +For element fields that take a handler like `onclick` or `oninput`, Dioxus will let you attach a closure. Alternatively, you can also pass a string using normal attribute syntax and assign this attribute on the DOM. + +This lets you escape into JavaScript (only if your renderer can execute JavaScript). + +```rust +rsx!{ + div { + // handle oninput with rust + oninput: move |_| {}, + + // or handle oninput with javascript + oninput: "alert('hello world')", + } +} + +``` + +## Wrapping up diff --git a/docs/guide/src/elements/vnodes.md b/docs/guide/src/elements/vnodes.md index b407ed02f..5ccae75f3 100644 --- a/docs/guide/src/elements/vnodes.md +++ b/docs/guide/src/elements/vnodes.md @@ -81,13 +81,13 @@ let name = "Bob"; rsx! ( "hello {name}" ) ``` -Unfortunately, you cannot drop in arbitrary expressions directly into the string literal. In the cases where we need to compute a complex value, we'll want to use `format_args!` directly. Due to specifics of how the `rsx!` macro (we'll cover later), our call to `format_args` must be contained within curly braces *and* square braces. +Unfortunately, you cannot drop in arbitrary expressions directly into the string literal. In the cases where we need to compute a complex value, we'll want to use `format_args!` directly. Due to specifics of how the `rsx!` macro (we'll cover later), our call to `format_args` must be contained within square braces. ```rust -rsx!( {[format_args!("Hello {}", if enabled { "Jack" } else { "Bob" } )]} ) +rsx!( {format_args!("Hello {}", if enabled { "Jack" } else { "Bob" } )] ) ``` -Alternatively, `&str` can be included directly, though it must be inside of an array: +Alternatively, `&str` can be included directly, though it must be inside of square braces: ```rust rsx!( "Hello ", [if enabled { "Jack" } else { "Bob" }] ) diff --git a/docs/guide/src/interactivity/user_input.md b/docs/guide/src/interactivity/user_input.md index 2424e7f14..746443071 100644 --- a/docs/guide/src/interactivity/user_input.md +++ b/docs/guide/src/interactivity/user_input.md @@ -1 +1,3 @@ # User Input and Controlled Components + +Handling user input is one of the most common things your app will do, but it can be tricky diff --git a/docs/guide/src/state/index.md b/docs/guide/src/state/index.md index 9b3e054fc..3740dd29d 100644 --- a/docs/guide/src/state/index.md +++ b/docs/guide/src/state/index.md @@ -4,5 +4,22 @@ Every app you'll build with Dioxus will have some sort of state that needs to be In this chapter, we'll cover the various ways to manage state, the appropriate terminology, various patterns, and then take an overview - ## Terminology + + + + + + +## Important hook: `use_state` + + + + + +## Important hook: `use_ref` + + + + +## `provide_context` and `consume_context` From 1560e2daca086f7914b5e371ff15425db53e5afd Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 11 Jan 2022 01:11:47 -0500 Subject: [PATCH 2/9] docs: add more docs --- docs/guide/.vscode/spellright.dict | 1 + docs/guide/src/SUMMARY.md | 7 +- docs/guide/src/async/fetching.md | 3 + docs/guide/src/async/index.md | 3 + docs/guide/src/elements/composing.md | 43 ++++++ docs/guide/src/elements/lists.md | 45 ++---- docs/guide/src/elements/propsmacro.md | 145 ++++++++++++++++++ docs/guide/src/elements/special_attributes.md | 25 ++- .../guide/src/interactivity/event_handlers.md | 2 + docs/guide/src/interactivity/lifecycles.md | 2 + docs/guide/src/interactivity/user_input.md | 3 + docs/guide/src/state/errorhandling.md | 1 + docs/guide/src/state/index.md | 1 + docs/guide/src/state/liftingstate.md | 3 + docs/guide/src/state/localstate.md | 3 + docs/guide/src/state/sharedstate.md | 6 + docs/guide/src/tutorial/components.md | 2 + docs/guide/src/tutorial/state.md | 2 + docs/guide/src/tutorial/structure.md | 3 + docs/guide/src/tutorial/styling.md | 3 + 20 files changed, 259 insertions(+), 44 deletions(-) create mode 100644 docs/guide/src/elements/composing.md 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
    @@ -59,38 +70,6 @@ The HTML-rendered version of this list would follow what you would expect:
``` -### 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! πŸ— From baf722de11efca34706e11fc80bcdc28efcabf5d Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 15 Jan 2022 20:14:51 -0500 Subject: [PATCH 3/9] docs: add a roadmap --- .vscode/spellright.dict | 2 + docs/guide/src/ROADMAP.md | 114 ++++++++++++++++++++++++++++++++++++++ docs/guide/src/SUMMARY.md | 1 + docs/guide/src/setup.md | 31 ++++++++--- 4 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 docs/guide/src/ROADMAP.md diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict index bdaf7abb8..1333ce02e 100644 --- a/.vscode/spellright.dict +++ b/.vscode/spellright.dict @@ -69,3 +69,5 @@ Jank noderef reborrow VirtualDoms +bootstrapper +WebkitGtk diff --git a/docs/guide/src/ROADMAP.md b/docs/guide/src/ROADMAP.md new file mode 100644 index 000000000..4f78e6043 --- /dev/null +++ b/docs/guide/src/ROADMAP.md @@ -0,0 +1,114 @@ +# Roadmap & Feature-set + +Before we dive into Dioxus, feel free to take a look at our feature set and roadmap to see if what Dioxus can do today works for you. + +If a feature that you need doesn't exist or you want to contribute to projects on the roadmap, feel free to get involved by [joining the discord](https://discord.gg/XgGxMSkvUM). + +Generally, here's the status of each platform: + +- **Web**: Dioxus is a great choice for pure web-apps - especially for CRUD/complex apps. However, it does lack the ecosystem of React, so you might be missing a component library or some useful hook. + +- **SSR**: Dioxus is a great choice for pre-rendering, hydration, and rendering HTML on a web endpoint. Be warned - the VirtualDom is not (currently) `Send + Sync`. + +- **Desktop**: You can build very competent single-window desktop apps right now. However, multi-window apps require support from Dioxus core and are not ready. + +- **Mobile**: Mobile support is very young. You'll be figuring things out as you go and there are not many support crates for peripherals. + +- **LiveView**: LiveView support is very young. You'll be figuring things out as you go. Thankfully, none of it is too hard and any work can be upstreamed into Dioxus. + +## Features +--- + +| Feature | Status | Description | +| ------------------------- | ------ | -------------------------------------------------------------------- | +| Conditional Rendering | βœ… | if/then to hide/show component | +| Map, Iterator | βœ… | map/filter/reduce to produce rsx! | +| Keyed Components | βœ… | advanced diffing with keys | +| Web | βœ… | renderer for web browser | +| Desktop (webview) | βœ… | renderer for desktop | +| Shared State (Context) | βœ… | share state through the tree | +| Hooks | βœ… | memory cells in components | +| SSR | βœ… | render directly to string | +| Component Children | βœ… | cx.children() as a list of nodes | +| Headless components | βœ… | components that don't return real elements | +| Fragments | βœ… | multiple elements without a real root | +| Manual Props | βœ… | Manually pass in props with spread syntax | +| Controlled Inputs | βœ… | stateful wrappers around inputs | +| CSS/Inline Styles | βœ… | syntax for inline styles/attribute groups | +| Custom elements | βœ… | Define new element primitives | +| Suspense | βœ… | schedule future render from future/promise | +| Integrated error handling | βœ… | Gracefully handle errors with ? syntax | +| NodeRef | βœ… | gain direct access to nodes | +| Re-hydration | βœ… | Pre-render to HTML to speed up first contentful paint | +| Jank-Free Rendering | βœ… | Large diffs are segmented across frames for silky-smooth transitions | +| Effects | βœ… | Run effects after a component has been committed to render | +| Portals | πŸ›  | Render nodes outside of the traditional tree structure | +| Cooperative Scheduling | πŸ›  | Prioritize important events over non-important events | +| Server Components | πŸ›  | Hybrid components for SPA and Server | +| Bundle Splitting | πŸ‘€ | Efficiently and asynchronously load the app | +| Lazy Components | πŸ‘€ | Dynamically load the new components as the page is loaded | +| 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 | + +- βœ… = implemented and working +- πŸ›  = actively being worked on +- πŸ‘€ = not yet implemented or being worked on +- ❓ = not sure if will or can implement + + +## Roadmap +--- + + +Core: +- [x] Release of Dioxus Core +- [x] Upgrade documentation to include more theory and be more comprehensive +- [ ] Support for HTML-side templates for lightning-fast dom manipulation +- [ ] Support for multiple renderers for same virtualdom (subtrees) +- [ ] Support for ThreadSafe (Send + Sync) +- [ ] Support for Portals + +SSR +- [x] SSR Support + Hydration +- [ ] Integrated suspense support for SSR + +Desktop +- [ ] Declarative window management +- [ ] Templates for building/bundling +- [ ] Fully native renderer +- [ ] Access to Canvas/WebGL context natively + +Mobile +- [ ] Mobile standard library + - [ ] GPS + - [ ] Camera + - [ ] filesystem + - [ ] Biometrics + - [ ] WiFi + - [ ] Bluetooth + - [ ] Notifications + - [ ] Clipboard + - [ ] + +Bundling (CLI) +- [x] translation from HTML into RSX +- [ ] dev server +- [ ] live reload +- [ ] translation from JSX into RSX +- [ ] hot module replacement +- [ ] code splitting +- [ ] asset macros +- [ ] css pipeline +- [ ] image pipeline + +Essential hooks +- [ ] Router +- [ ] Global state management +- [ ] Resize observer + + diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index 54d9fcd4c..d5b7055b5 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -1,6 +1,7 @@ # Summary - [Introduction](README.md) +- [Roadmap](ROADMAP.md) - [Getting Setup](setup.md) - [Hello, World!](hello_world.md) - [Describing the UI](elements/index.md) diff --git a/docs/guide/src/setup.md b/docs/guide/src/setup.md index 9b49f6cb5..4aebc69fd 100644 --- a/docs/guide/src/setup.md +++ b/docs/guide/src/setup.md @@ -20,7 +20,7 @@ Dioxus requires a few main things to get up and running: Dioxus integrates very well with the Rust-Analyzer IDE plugin which will provide appropriate syntax highlighting, code navigation, folding, and more. -### Installing Rust +## Installing Rust Head over to [https://rust-lang.org](http://rust-lang.org) and install the Rust compiler. @@ -30,24 +30,39 @@ Once installed, make sure to install wasm32-unknown-unknown as a target if you' rustup target add wasm32-unknown-unknown ``` -### Platform-Specific Dependencies +## Platform-Specific Dependencies If you are running a modern, mainstream operating system, you should need no additional setup to build WebView-based Desktop apps. However, if you are running an older version of Windows or a flavor of linux with no default web rendering engine, you might need to install some additional dependencies. -For windows users: download the [bootstrapper for Webview2 from Microsoft](https://developer.microsoft.com/en-us/microsoft-edge/webview2/) -For linux users, we need the development libraries for libgtk. +### Windows -```` +Windows Desktop apps depend on WebView2 - a library which should be installed in all modern Windows distributions. If you have Edge installed, then Dioxus will work fine. If you *don't* have Webview2, [then you can install it through Microsoft](https://developer.microsoft.com/en-us/microsoft-edge/webview2/). MS provides 3 options: + +- 1) A tiny "evergreen" *bootstrapper* which will fetch an installer from Microsoft's CDN +- 2) A tiny *installer* which will fetch Webview2 from Microsoft's CDN +- 3) A statically linked version of Webview2 in your final binary for offline users + +For development purposes, use Option 1. + +### Linux + +Webview Linux apps require WebkitGtk. When distributing, this can be part of your dependency tree in your `.rpm` or `.deb`. However, it's very likely that your users will already have WebkitGtk. + +``` sudo apt install libwebkit2gtk-4.0-dev libgtk-3-dev libappindicator3-dev ``` -When distributing onto older Windows platforms or less-mainstream + +If you run into issues, make sure you have all the basics installed, as outlined in the [Tauri docs](https://tauri.studio/en/docs/get-started/setup-linux). +### macOS + +Currently - everything for macOS is built right in! However, you might run into an issue if you're using nightly Rust due to some permissions issues in our Tao dependency (which have been resolved but not published). -### Dioxus-CLI for dev server, bundling, etc. +## Dioxus-CLI for dev server, bundling, etc. We also recommend installing the Dioxus CLI. The Dioxus CLI automates building and packaging for various targets and integrates with simulators, development servers, and app deployment. To install the CLI, you'll need cargo (should be automatically installed with Rust): @@ -63,7 +78,7 @@ $ cargo install --force dioxus-cli We provide this 1st-party tool to save you from having to run potentially untrusted code every time you add a crate to your project - as is standard in the NPM ecosystem. -### Suggested extensions +## Suggested extensions If you want to keep your traditional `npm install XXX` workflow for adding packages, you might want to install `cargo-edit` and a few other fun `cargo` extensions: From 1c6d6421dc4c285751fda8666c1e400021006fdb Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 24 Jan 2022 03:01:04 -0500 Subject: [PATCH 4/9] wip: add router --- docs/router/.gitignore | 1 + docs/router/book.toml | 6 ++++++ docs/router/src/SUMMARY.md | 3 +++ docs/router/src/chapter_1.md | 1 + 4 files changed, 11 insertions(+) create mode 100644 docs/router/.gitignore create mode 100644 docs/router/book.toml create mode 100644 docs/router/src/SUMMARY.md create mode 100644 docs/router/src/chapter_1.md diff --git a/docs/router/.gitignore b/docs/router/.gitignore new file mode 100644 index 000000000..7585238ef --- /dev/null +++ b/docs/router/.gitignore @@ -0,0 +1 @@ +book diff --git a/docs/router/book.toml b/docs/router/book.toml new file mode 100644 index 000000000..216bc555e --- /dev/null +++ b/docs/router/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["Jonathan Kelley"] +language = "en" +multilingual = false +src = "src" +title = "Dioxus Router" diff --git a/docs/router/src/SUMMARY.md b/docs/router/src/SUMMARY.md new file mode 100644 index 000000000..7390c8289 --- /dev/null +++ b/docs/router/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +- [Chapter 1](./chapter_1.md) diff --git a/docs/router/src/chapter_1.md b/docs/router/src/chapter_1.md new file mode 100644 index 000000000..b743fda35 --- /dev/null +++ b/docs/router/src/chapter_1.md @@ -0,0 +1 @@ +# Chapter 1 From 4fb2221ee18d85c1f4b7bbcbf06c1b962d42bbbe Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 28 Jan 2022 02:44:13 -0500 Subject: [PATCH 5/9] docs: buff up the theory of react --- .vscode/spellright.dict | 1 + docs/guide/src/SUMMARY.md | 2 +- docs/guide/src/elements/component_children.md | 4 +- docs/guide/src/elements/components.md | 2 +- docs/guide/src/elements/composing.md | 244 ++++++++++++++++-- .../src/elements/exporting_components.md | 11 +- docs/guide/src/elements/lists.md | 20 +- docs/guide/src/elements/special_attributes.md | 8 +- docs/guide/src/elements/vnodes.md | 2 +- docs/guide/src/images/oldnew.png | Bin 0 -> 124296 bytes docs/guide/src/setup.md | 6 +- packages/desktop/examples/async.rs | 8 +- 12 files changed, 263 insertions(+), 45 deletions(-) create mode 100644 docs/guide/src/images/oldnew.png diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict index 1333ce02e..3b67f08b6 100644 --- a/.vscode/spellright.dict +++ b/.vscode/spellright.dict @@ -71,3 +71,4 @@ reborrow VirtualDoms bootstrapper WebkitGtk +laymans diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index 32d3eb88b..9772f6b54 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -13,7 +13,7 @@ - [Properties](elements/propsmacro.md) - [Reusing, Importing, and Exporting](elements/exporting_components.md) - [Children and Attributes](elements/component_children.md) - - [Composing Components](elements/composing.md) + - [Theory of React](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/elements/component_children.md b/docs/guide/src/elements/component_children.md index d99273394..52f116881 100644 --- a/docs/guide/src/elements/component_children.md +++ b/docs/guide/src/elements/component_children.md @@ -137,7 +137,7 @@ fn clickable(cx: Scope) -> Element { } ``` -## Passing attributes + ## Passing handlers diff --git a/docs/guide/src/elements/components.md b/docs/guide/src/elements/components.md index ea43034d9..8b284b793 100644 --- a/docs/guide/src/elements/components.md +++ b/docs/guide/src/elements/components.md @@ -106,7 +106,7 @@ fn Post(cx: Scope) -> Element { } ``` -When declaring a component in `rsx!`, we can pass in properties using the traditional Rust struct syntax. Dioxus will automatically call "into" on the property fields, cloning when necessary. Our `Post` component is simply a collection of smaller components wrapped together in a single container. +When declaring a component in `rsx!`, we can pass in properties using the traditional Rust struct syntax. Our `Post` component is simply a collection of smaller components wrapped together in a single container. Let's take a look at the `VoteButton` component. For now, we won't include any interactivity - just the rendering the score and buttons to the screen. diff --git a/docs/guide/src/elements/composing.md b/docs/guide/src/elements/composing.md index e4b00de2e..e6ccf189e 100644 --- a/docs/guide/src/elements/composing.md +++ b/docs/guide/src/elements/composing.md @@ -1,43 +1,243 @@ -# Composing Components +# Thinking in React -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. +We've finally reached the point in our tutorial where we can talk about the "Theory of React." We've talked about defining a declarative view, but not about the aspects that make our code *reactive*. + +Understanding the theory of reactive program is essential to making sense of Dioxus and writing effective, performant UIs. In this section, we'll talk about: -- Sharing data between components -- How the UI is updated from input and state changes +- One-way data flow +- Modifying data - Forcing renders - How renders propagate -- +This section is a bit long, but worth the read. We recommend coffee, tea, and/or snacks. -### Rendering our posts with a PostList component +## Reactive Programming -Let's start by modeling this problem with a component and some properties. +Dioxus is one the very few Rust libraries that provide a "Reactive Programming Model". The term "Reactive programming" is a classification of programming paradigm - much like functional or imperative programming. This is a very important distinction since it affects how we *think* about our code. -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. +Reactive programming is programming model concerned with deriving computations from asynchronous data flow. Most reactive programs are comprised of a handful of datasources, intermediate computations, and a final result. + +We can consider the our rendered GUI to be the final result of reactive app and our datasources to include shared contexts and component properties. + +For example, the model presented in the figure below is comprised of two data sources: time and a constant. These values are passed through our computation graph to achieve a final result: `g`. + +![Reactive Model](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Reactive_programming_glitches.svg/440px-Reactive_programming_glitches.svg.png) + +Whenever our `seconds` variable changes, then we will reevaluate the computation for `t`. Because `g` relies on `t`, then we will also reevaluate its computation too. Notice that we would've reevaluated the computation for `g` even if `t` didn't change because `seconds` is used to calculate `g`. + +However, if we somehow changed our constant from `1` to `2`, then we need to reevaluate `t`. If, for whatever reason, this change did not affect the result of `t`, then we wouldn't try to reevaluate `g`. + +In Reactive Programming, we don't think about whether or not we should reevaluate `t` or `g`; instead, we simply provide functions of computation and let the framework figure out the rest for us. + +In Rust, our reactive app would look something like: ```rust -#[derive(Props, PartialEq)] -struct PostListProps<'a> { - posts: &'a [PostData] +fn compute_g(t: i32, seconds: i32) -> bool { + t > seconds +} + +fn compute_t(constant: i32, seconds: i32) -> i32 { + constant + seconds +} + +fn compute_graph(constant: i32, seconds: i32) -> bool { + let t = compute_t(constant, seconds); + let g = compute_g(t, seconds); + g } ``` -Next, we're going to define our component: + +## How is Dioxus Reactive? + +The Dioxus VirtualDom provides us a framework for reactive programming. When we build apps with dioxus, we need to feed it our own custom datasources. This can be either initial props or some values fetched from the network. We then pass this data through our app into components through properties. + +If we represented the reactive graph presented above in Dioxus, it would look very similar: ```rust -fn App(cx: Scope) -> Element { +// Declare a component that holds our datasources and calculates `g` +fn RenderGraph(cx: Scope) -> Element { + let seconds = use_datasource(SECONDS); + let constant = use_state(&cx, || 1); + + cx.render(rsx!( + RenderG { seconds: seconds } + RenderT { seconds: seconds, constant: constant } + )) +} + +// "calculate" g by rendering `t` and `seconds` +#[inline_props] +fn RenderG(cx: Scope, seconds: i32) -> Element { + cx.render(rsx!{ "There are {seconds} seconds remaining..." }) +} + +// calculate and render `t` in its own component +#[inline_props] +fn RenderT(cx: Scope, seconds: i32, constant: i32) -> Element { + let res = seconds + constant; + cx.render(rsx!{ "{res}" }) +} +``` + +With this app, we've defined three components. Our top-level component provides our datasources (the hooks), computation nodes (child components), and a final value (what's "rendered"). + +Now, whenever the `constant` changes, our `RenderT` component will be re-rendered. However, if `seconds` doesn't change, then we don't need to re-render `RenderG` because the input is the same. If `seconds` *does* change, then both RenderG and RenderT will be reevaluated. + +Dioxus is "Reactive" because it provides this framework for us. All we need to do is write our own tiny units of computations and Dioxus figures out which components need to be reevaluated automatically. + +These extra checks and algorithms add some overhead, which is why you see projects like [Sycamore](http://sycamore-rs.netlify.app) and [SolidJS](http://solidjs.com) eliminating them altogether. Dioxus is *really* fast, so we're willing to exchange the added overhead for improved developer experience. + +## How do we update values in our dataflow graph? + +Dioxus will automatically figure out how to regenerate parts of our app when datasources change. But how exactly can we update our data sources? + +In Dioxus there are two datasources: + +1. Local state in `use_hook` and all other hooks +2. Global state through `provide_context`. + +Technically, the root props of the VirtualDom are a third datasource, but since we cannot modify them, they are not worth talking about. + +### Local State + +For local state in hooks, Dioxus gives us the `use_hook` method which returns an `&mut T` without any requirements. This means raw hook values are not tracked by Dioxus. In fact, we could write a component that modifies a hook value directly: + +```rust +fn app(cx: Scope) -> Element { + let mut count = cx.use_hook(|_| 0); 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 - } - }) + button { + onclick: move |_| *count += 1, + "Count: {count}" } }) } ``` + +However, when this value is written to, the component does not know to be reevaluated. We must explicitly tell Dioxus that this component is "dirty" and needs to be re-rendered. This is done through the `cx.needs_update` method: + +```rust +button { + onclick: move |_| { + *count += 1; + cx.needs_update(); + }, + "Count: {count}" +} +``` + +Now, whenever we click the button, the value will change and the component will be re-rendered. + +> Re-rendering is when Dioxus calls your function component *again*. Component functions will be called over and over throughout their lifetime, so they should be mostly side-effect free. + +### Understand this! + +Your component functions will be called ("rendered" in our lingo) for as long as the component is present in the tree. + +A single component will be called multiple times, modifying its own internal state or rendering new nodes with new values from its properties. + +### App-Global State + +With the `provide_context` and `consume_context` methods on `Scope`, we can share values to descendants without having to pass values through component props. This has the side-effect of making our datasources less obvious from a high-level perspective, but it makes our components more modular within the same codebase. + +To make app-global state easier to reason about, Dioxus makes all values provided through `provide_context` immutable. This means any library built on top of `provide_context` needs to use interior mutability to modify share global state. + +In these cases, App-Global state needs to manually track which components need to be re-generated. + +To regenerate *any* component in your app, you can get a handle the Dioxus' internal scheduler through `schedule_update_any`: + +```rust +let force_render = cx.schedule_update_any(); + +// force a render of the root component +force_render(ScopeId(0)); +``` + +## What does it mean for a component to "re-render" + +In our guides, we frequently use the phrase "re-render" to describe updates to our app. You'll often hear this paired with "preventing unnecessary re-renders." But what exactly does this mean? + +When we call `dioxus::desktop::launch`, Dioxus will create a new `Scope` object and call the component we gave it. Our `rsx!` calls will create new nodes which we return back to the VirtualDom. Dioxus will then look through these nodes for child components, call their functions, and so on until every component has been "rendered." We consider these nodes "rendered" because they were created because of our explicit actions. + +The tree of UI that dioxus creates will roughly look like the tree of components presented earlier: + +![Tree of UI](../images/component_tree.png) + +But what happens when we call `needs_update` after modifying some important state? Well, if Dioxus called our component's function again, then we would produce new, different nodes. In fact, this is exactly what Dioxus does! + +At this point, we have some old nodes and some new nodes. Again, we call this "rendering" because Dioxus had to create new nodes because of our explicit actions. Any time new nodes get created, our VirtualDom is being "rendered." + +These nodes are stored in an extremely efficient memory allocator called a "bump arena." For example, a div with a handler and attribute would be stored in memory in two locations: the "old" tree and the "new" tree. + +![Bump Arenas](../images/oldnew.png) + +From here, Dioxus computes the difference between these trees and updates the Real DOM to make it look like the new version of what we've declared. + +![Diffing](../images/diffing.png) + +## Suppressing Renders + +So, we know how to make Dioxus render, but how do we *stop* it? What if we *know* that our state didn't change and we shouldn't render and diff new nodes because they'll be exactly the same as the last time? + +In these cases, you want to reach for *memoization*. In Dioxus, memoization involves preventing a component from rendering again if its props didn't change since the last time it attempted to render. + +Visually, you can tell that a component will only re-render if the new value is sufficiently different than the old one. + +| props.val | re-render | +| --------- | --------- | +| 10 | true | +| 20 | true | +| 20 | false | +| 20 | false | +| 10 | true | +| 30 | false | + +This is why when you `derive(Props)`, you must also implement the `PartialEq` trait. To override the memoization strategy for a component, you can simply implement your own PartialEq. + +```rust +struct CustomProps { + val: i32, +} + +impl PartialEq for CustomProps { + fn partial_eq(&self, other: &Self) -> bool { + // we don't render components that have a val less than 5 + if other.val > 5 && self.val > 5{ + self.val == other.val + } + } +} +``` + +However, for components that borrow data, it doesn't make sense to implement PartialEq since the actual references in memory might be different. + +You can technically override this behavior by implementing the `Props` trait manually, though it's unsafe and easy to mess up: + +```rust +impl Properties for CustomProps { + fn memoize(&self, other &Self) -> bool { + self != other + } +} +``` + +TLDR: +- Dioxus checks if props changed between renders +- If props changed according to PartialEq, Dioxus re-renders the component +- Props that have a lifetime (ie `<'a>`) will always be re-rendered + +## Wrapping Up + +Wow, that was a lot of material! + +Let's see if we can recap what was presented: + +- Reactive programming calculates a final value from datasources and computation +- Dioxus is "reactive" since it figures out which computations to check +- `schedule_update` must be called to mark a component as dirty +- dirty components will be re-rendered (called multiple times) to produce a new UI +- Renders can be suppressed with memoization + +This theory is crucial to understand how to compose components and how to control renders in your app. diff --git a/docs/guide/src/elements/exporting_components.md b/docs/guide/src/elements/exporting_components.md index 117249db8..350484c95 100644 --- a/docs/guide/src/elements/exporting_components.md +++ b/docs/guide/src/elements/exporting_components.md @@ -66,6 +66,14 @@ fn ActionCard(Scope) -> Element {} We should also create a `mod.rs` file in the `post` folder so we can use it from our `main.rs`. Our `Post` component and its props will go into this file. +```rust +use dioxus::prelude::*; + +#[derive(PartialEq, Props)] +struct PostProps {} +fn Post(Scope) -> Element {} +``` + ```shell β”œβ”€β”€ Cargo.toml └── src @@ -78,8 +86,6 @@ We should also create a `mod.rs` file in the `post` folder so we can use it from └── mod.rs ``` - - In our `main.rs`, we'll want to declare the `post` module so we can access our `Post` component. ```rust @@ -164,7 +170,6 @@ pub fn Post(Scope) -> Element { } ``` - Ultimately, including and exporting components is governed by Rust's module system. [The Rust book is a great resource to learn about these concepts in greater detail.](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html) ## Final structure: diff --git a/docs/guide/src/elements/lists.md b/docs/guide/src/elements/lists.md index 4c56c3d88..41c6a892d 100644 --- a/docs/guide/src/elements/lists.md +++ b/docs/guide/src/elements/lists.md @@ -10,14 +10,24 @@ In this chapter, you will learn: ## Rendering data from lists -Thinking back to our analysis of the `r/reddit` page, we notice a list of data that needs to be rendered: the list of posts. This list of posts is always changing, so we cannot just hardcode the lists into our app like: +If we wanted to build the Reddit app, then we need to implement a list of data that needs to be rendered: the list of posts. This list of posts is always changing, so we cannot just hardcode the lists into our app directly, like so: ```rust +// we shouldn't ship our app with posts that don't update! rsx!( div { - Post {/* some properties */} - Post {/* some properties */} - Post {/* some properties */} + Post { + title: "Post A", + votes: 120, + } + Post { + title: "Post B", + votes: 14, + } + Post { + title: "Post C", + votes: 999, + } } ) ``` @@ -168,7 +178,7 @@ File names in a folder and Element keys in an array serve a similar purpose. The ### Gotcha You might be tempted to use an item’s index in the array as its key. In fact, that’s what Dioxus will use if you don’t specify a key at all. But the order in which you render items will change over time if an item is inserted, deleted, or if the array gets reordered. Index as a key often leads to subtle and confusing bugs. -Similarly, do not generate keys on the fly, `gen_random`. This will cause keys to never match up between renders, leading to all your components and DOM being recreated every time. Not only is this slow, but it will also lose any user input inside the list items. Instead, use a stable ID based on the data. +Similarly, do not generate keys on the fly, like `gen_random`. This will cause keys to never match up between renders, leading to all your components and DOM being recreated every time. Not only is this slow, but it will also lose any user input inside the list items. Instead, use a stable ID based on the data. Note that your components won’t receive key as a prop. It’s only used as a hint by Dioxus itself. If your component needs an ID, you have to pass it as a separate prop: ```rust diff --git a/docs/guide/src/elements/special_attributes.md b/docs/guide/src/elements/special_attributes.md index 9a1122a3e..460d1cd69 100644 --- a/docs/guide/src/elements/special_attributes.md +++ b/docs/guide/src/elements/special_attributes.md @@ -7,7 +7,7 @@ In this section, we'll cover special attributes built into Dioxus: - `dangerous_inner_html` - Boolean attributes - `prevent_default` -- `..Attributes` + - event handlers as string attributes - `value`, `checked`, and `selected` @@ -104,9 +104,11 @@ rsx!{ } } ``` - + ## Controlled inputs and `value`, `checked`, and `selected` diff --git a/docs/guide/src/elements/vnodes.md b/docs/guide/src/elements/vnodes.md index f8959c123..3c136cfc2 100644 --- a/docs/guide/src/elements/vnodes.md +++ b/docs/guide/src/elements/vnodes.md @@ -170,4 +170,4 @@ We learned: - Elements can either be a named container or text - Some Elements have properties that the renderer can use to draw the UI to the screen -Next, we'll compose Elements together to form components. +Next, we'll compose Elements together using Rust-based logic. diff --git a/docs/guide/src/images/oldnew.png b/docs/guide/src/images/oldnew.png new file mode 100644 index 0000000000000000000000000000000000000000..5aca37e4079cf9984fe7341823406dd7d36b4ebb GIT binary patch literal 124296 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 Element { - let count = use_state(&cx, || 0); + let (count, set_count) = use_state(&cx, || 0); use_future(&cx, || { - let count = count.for_async(); + to_owned![set_count]; async move { loop { tokio::time::sleep(Duration::from_millis(1000)).await; - *count.modify() += 1; + set_count.modify(|v| v + 1) } } }); @@ -26,7 +26,7 @@ fn app(cx: Scope) -> Element { div { h1 { "High-Five counter: {count}" } button { - onclick: move |_| count.set(0), + onclick: move |_| set_count(0), "Click me!" } } From 564284f4be9b2931cd49f7e5435e0e8368fddd53 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 28 Jan 2022 03:04:21 -0500 Subject: [PATCH 6/9] docs: add rules of hooks --- .vscode/spellright.dict | 1 + docs/guide/src/SUMMARY.md | 5 +- docs/guide/src/interactivity/hooks.md | 73 +++++++++++++++++++ .../guide/src/interactivity/importanthooks.md | 23 ++++++ docs/guide/src/interactivity/index.md | 20 +++-- 5 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 docs/guide/src/interactivity/importanthooks.md diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict index 3b67f08b6..fb7eef771 100644 --- a/.vscode/spellright.dict +++ b/.vscode/spellright.dict @@ -72,3 +72,4 @@ VirtualDoms bootstrapper WebkitGtk laymans +iter diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index 9772f6b54..5a8fadb96 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -13,10 +13,11 @@ - [Properties](elements/propsmacro.md) - [Reusing, Importing, and Exporting](elements/exporting_components.md) - [Children and Attributes](elements/component_children.md) - - [Theory of React](elements/composing.md) + - [Theory of Reactive Programming](elements/composing.md) - [Adding Interactivity](interactivity/index.md) - [Hooks and Internal State](interactivity/hooks.md) - - [Event handlers](interactivity/event_handlers.md) + - [Event Listeners](interactivity/event_handlers.md) + - [UseState and UseRef](interactivity/importanthooks.md) - [User Input and Controlled Components](interactivity/user_input.md) - [Lifecycle, updates, and effects](interactivity/lifecycles.md) - [Managing State](state/index.md) diff --git a/docs/guide/src/interactivity/hooks.md b/docs/guide/src/interactivity/hooks.md index adf43c6ef..b9ec32e58 100644 --- a/docs/guide/src/interactivity/hooks.md +++ b/docs/guide/src/interactivity/hooks.md @@ -76,6 +76,78 @@ This is why hooks called out of order will fail - if we try to downcast a `Hook< This pattern might seem strange at first, but it can be a significant upgrade over structs as blobs of state, which tend to be difficult to use in [Rust given the ownership system](https://rust-lang.github.io/rfcs/2229-capture-disjoint-fields.html). + +## Rules of hooks + +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): + +- Functions with "use_" should not be called in callbacks +- Functions with "use_" should not be called out of order +- Functions with "use_" should not be called in loops or conditionals + +Examples of "no-nos" include: + +### ❌ Nested uses + +```rust +// ❌ don't call use_hook or any `use_` function *inside* use_hook! +cx.use_hook(|_| { + let name = cx.use_hook(|_| "ads"); +}) + +// βœ… instead, move the first hook above +let name = cx.use_hook(|_| "ads"); +cx.use_hook(|_| { + // do something with name here +}) +``` + +### ❌ Uses in conditionals +```rust +// ❌ don't call use_ in conditionals! +if do_thing { + let name = use_state(&cx, || 0); +} + +// βœ… instead, *always* call use_state but leave your logic +let name = use_state(&cx, || 0); +if do_thing { + // do thing with name here +} +``` + +### ❌ Uses in loops + + +```rust +// ❌ Do not use hooks in loops! +let mut nodes = vec![]; + +for name in names { + let age = use_state(&cx, |_| 0); + nodes.push(cx.render(rsx!{ + div { "{age}" } + })) +} + +// βœ… Instead, consider refactoring your usecase into components +#[inline_props] +fn Child(cx: Scope, name: String) -> Element { + let age = use_state(&cx, |_| 0); + cx.render(rsx!{ div { "{age}" } }) +} + +// βœ… Or, use a hashmap with use_ref +```rust +let ages = use_ref(&cx, |_| HashMap::new()); + +names.iter().map(|name| { + let age = ages.get(name).unwrap(); + cx.render(rsx!{ div { "{age}" } }) +}) +``` + ## Building new Hooks However, most hooks you'll interact with *don't* return an `&mut T` since this is not very useful in a real-world situation. @@ -168,6 +240,7 @@ fn example(cx: Scope) -> Element { ``` + ## Hooks provided by the `Dioxus-Hooks` package By default, we bundle a handful of hooks in the Dioxus-Hooks package. Feel free to click on each hook to view its definition and associated documentation. diff --git a/docs/guide/src/interactivity/importanthooks.md b/docs/guide/src/interactivity/importanthooks.md new file mode 100644 index 000000000..91d50cab9 --- /dev/null +++ b/docs/guide/src/interactivity/importanthooks.md @@ -0,0 +1,23 @@ +# `use_state` and `use_ref` + +Most components you will write in Dioxus will need to store state somehow. For local state, we provide two very convenient hooks: + +- `use_state` +- `use_ref` + +Both of these hooks are extremely powerful and flexible, so we've dedicated this section to understanding them properly. + +## Note on Hooks + +If you're struggling with errors due to usage in hooks, make sure you're following the rules of hooks: + + +## `use_state` + +The `use_state` hook is very similar to its React counterpart. + + + + +## `use_ref` + diff --git a/docs/guide/src/interactivity/index.md b/docs/guide/src/interactivity/index.md index 0e1a61252..b71a3e73d 100644 --- a/docs/guide/src/interactivity/index.md +++ b/docs/guide/src/interactivity/index.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: Scope)-> Element { - let post = use_state(&cx, || { + let (post, set_post) = use_state(&cx, || { PostData { id: Uuid::new_v4(), score: 10, @@ -84,10 +84,10 @@ fn App(cx: Scope)-> Element { } ``` -Whenever we have a new post that we want to render, we can call `set` on `post` and provide a new value: +Whenever we have a new post that we want to render, we can call `set_post` and provide a new value: ```rust -post.set(PostData { +set_post(PostData { id: Uuid::new_v4(), score: 20, comment_count: 0, @@ -128,7 +128,13 @@ We'll dive much deeper into event listeners later. ### Updating state asynchronously -We can also update our state outside of event listeners with `coroutines`. `Coroutines` are asynchronous blocks of our component that have the ability to cleanly interact with values, hooks, and other data in the component. Since coroutines stick around between renders, the data in them must be valid for the `'static` lifetime. We must explicitly declare which values our task will rely on to avoid the `stale props` problem common in React. +We can also update our state outside of event listeners with `futures` and `coroutines`. + +- `Futures` are Rust's version of promises that can execute asynchronous work by an efficient polling system. We can submit new futures to Dioxus either through `push_future` which returns a `TaskId` or with `spawn`. +- +- `Coroutines` are asynchronous blocks of our component that have the ability to cleanly interact with values, hooks, and other data in the component. + +Since coroutines and Futures stick around between renders, the data in them must be valid for the `'static` lifetime. We must explicitly declare which values our task will rely on to avoid the `stale props` problem common in React. We can use tasks in our components to build a tiny stopwatch that ticks every second. @@ -139,7 +145,7 @@ fn App(cx: Scope)-> Element { let mut sec_elapsed = use_state(&cx, || 0); use_future(&cx, || { - let mut sec_elapsed = sec_elapsed.for_async(); + to_owned![sec_elapsed]; // explicitly capture this hook for use in async async move { loop { TimeoutFuture::from_ms(1000).await; @@ -152,7 +158,7 @@ fn App(cx: Scope)-> Element { } ``` -Using asynchronous code can be difficult! This is just scratching the surface of what's possible. We have an entire chapter on using async properly in your Dioxus Apps. +Using asynchronous code can be difficult! This is just scratching the surface of what's possible. We have an entire chapter on using async properly in your Dioxus Apps. We have an entire section dedicated to using `async` properly later in this book. ### How do I tell Dioxus that my state changed? @@ -170,7 +176,7 @@ With these building blocks, we can craft new hooks similar to `use_state` that l In general, Dioxus should be plenty fast for most use cases. However, there are some rules you should consider following to ensure your apps are quick. -- 1) **Don't call setβ€”state _while rendering_**. This will cause Dioxus to unnecessarily re-check the component for updates. +- 1) **Don't call setβ€”state _while rendering_**. This will cause Dioxus to unnecessarily re-check the component for updates or enter an infinite loop. - 2) **Break your state apart into smaller sections.** Hooks are explicitly designed to "unshackle" your state from the typical model-view-controller paradigm, making it easy to reuse useful bits of code with a single function. - 3) **Move local state down**. Dioxus will need to re-check child components of your app if the root component is constantly being updated. You'll get best results if rapidly-changing state does not cause major re-renders. From b0fd034ae70a0fb373e62a8f5c10e5eaf207183b Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 28 Jan 2022 03:20:42 -0500 Subject: [PATCH 7/9] docs: add important hooks --- .vscode/spellright.dict | 3 + .../guide/src/interactivity/importanthooks.md | 119 +++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict index fb7eef771..7a5a261d7 100644 --- a/.vscode/spellright.dict +++ b/.vscode/spellright.dict @@ -73,3 +73,6 @@ bootstrapper WebkitGtk laymans iter +cloneable +fudamental +clonable diff --git a/docs/guide/src/interactivity/importanthooks.md b/docs/guide/src/interactivity/importanthooks.md index 91d50cab9..eff27b199 100644 --- a/docs/guide/src/interactivity/importanthooks.md +++ b/docs/guide/src/interactivity/importanthooks.md @@ -11,13 +11,130 @@ Both of these hooks are extremely powerful and flexible, so we've dedicated this If you're struggling with errors due to usage in hooks, make sure you're following the rules of hooks: +- Functions with "use_" should not be called in callbacks +- Functions with "use_" should not be called out of order +- Functions with "use_" should not be called in loops or conditionals + +A large majority of issues that seem to be "wrong with Dioxus" are actually just a misuse of hooks. ## `use_state` -The `use_state` hook is very similar to its React counterpart. +The `use_state` hook is very similar to its React counterpart. When we use it, we get two values: +- The value itself as an `&T` +- A handle to set the value `&UseState` +```rust +let (count, set_count) = use_state(&cx, || 0); +// then to set the count +set_count(count + 1) +``` + +However, the `set_count` object is more powerful than it looks. You can use it as a closure, but you can also call methods on it to do more powerful operations. + +For instance, we can get a handle to the current value at any time: + +```rust +let current: Rc = set_count.current(); +``` + +Or, we can get the inner handle to set the value + +```rust +let setter: Rc = set_count.setter(); +``` + +Or, we can set a new value using the current value as a reference: + +```rust +set_count.modify(|i| i + 1); +``` + +If the value is cheaply cloneable, then we can call `with_mut` to get a mutable reference to the value: + +```rust +set_count.with_mut(|i| *i += 1); +``` + +Alternatively, we can call `make_mut` which gives us a `RefMut` to the underlying value: + +```rust +*set_count.make_mut() += 1; +``` + +Plus, the `set_count` handle is cloneable into async contexts, so we can use it in futures. + +```rust +// count up infinitely +cx.spawn({ + to_owned![set_count]; + async move { + loop { + wait_ms(100).await; + set_count.modify(|i| i + 1); + } + } +}) +``` ## `use_ref` +You might've noticed a fundamental limitation to `use_state`: to modify the value in-place, it must be cheaply cloneable. But what if your type is not cheap to clone? + +In these cases, you should reach for `use_ref` which is essentially just a glorified `Rc>` (typical Rust UI shenanigans). + +This provides us some runtime locks around our data, trading reliability for performance. For most cases though, you will find it hard to make `use_ref` crash. + +> Note: this is *not* exactly like its React counterpart since calling `write` will cause a re-render. For more React parity, use the `write_silent` method. + +To use the hook: + +```rust +let names = use_ref(&cx, || vec!["susie", "calvin"]); +``` + +To read the hook values, use the `read()` method: + +```rust +cx.render(rsx!{ + ul { + names.read().iter().map(|name| { + rsx!{ "{name}" } + }) + } +}) +``` + +And then to write into the hook value, use the `write` method: + +```rust +names.write().push("Tiger"); +``` + +If you don't want to re-render the component when names is updated, then we can use the `write_silent` method: + +```rust +names.write().push("Transmogrifier"); +``` + +Again, like `UseState`, the `UseRef` handle is clonable into async contexts: + + +```rust +// infinitely push calvin into the list +cx.spawn({ + to_owned![names]; + async move { + loop { + wait_ms(100).await; + names.write().push("Calvin"); + } + } +}) +``` + + +## Wrapping up + +These two hooks are extremely powerful at storing state. From 3d75f1cb054ec051846fb5b72dd7b2e55ef3e242 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 28 Jan 2022 16:11:04 -0500 Subject: [PATCH 8/9] docs: fix some typos --- .vscode/spellright.dict | 3 +++ docs/guide/.vscode/spellright.dict | 3 --- docs/guide/src/SUMMARY.md | 5 +++-- docs/guide/src/elements/composing.md | 18 +++++++++--------- docs/guide/src/interactivity/index.md | 13 +++++-------- 5 files changed, 20 insertions(+), 22 deletions(-) delete mode 100644 docs/guide/.vscode/spellright.dict diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict index 7a5a261d7..06845ebf9 100644 --- a/.vscode/spellright.dict +++ b/.vscode/spellright.dict @@ -76,3 +76,6 @@ iter cloneable fudamental clonable +oninput +Webview +idanarye diff --git a/docs/guide/.vscode/spellright.dict b/docs/guide/.vscode/spellright.dict deleted file mode 100644 index 572d1f65c..000000000 --- a/docs/guide/.vscode/spellright.dict +++ /dev/null @@ -1,3 +0,0 @@ -oninput -Webview -idanarye diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index 5a8fadb96..d4990fc99 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -13,11 +13,11 @@ - [Properties](elements/propsmacro.md) - [Reusing, Importing, and Exporting](elements/exporting_components.md) - [Children and Attributes](elements/component_children.md) - - [Theory of Reactive Programming](elements/composing.md) + - [How Data Flows](elements/composing.md) - [Adding Interactivity](interactivity/index.md) - [Hooks and Internal State](interactivity/hooks.md) + - [UseState and UseRef](interactivity/importanthooks.md) - [Event Listeners](interactivity/event_handlers.md) - - [UseState and UseRef](interactivity/importanthooks.md) - [User Input and Controlled Components](interactivity/user_input.md) - [Lifecycle, updates, and effects](interactivity/lifecycles.md) - [Managing State](state/index.md) @@ -45,3 +45,4 @@ + diff --git a/docs/guide/src/elements/composing.md b/docs/guide/src/elements/composing.md index e6ccf189e..dcd54b18f 100644 --- a/docs/guide/src/elements/composing.md +++ b/docs/guide/src/elements/composing.md @@ -2,7 +2,7 @@ We've finally reached the point in our tutorial where we can talk about the "Theory of React." We've talked about defining a declarative view, but not about the aspects that make our code *reactive*. -Understanding the theory of reactive program is essential to making sense of Dioxus and writing effective, performant UIs. +Understanding the theory of reactive programming is essential to making sense of Dioxus and writing effective, performant UIs. In this section, we'll talk about: @@ -17,15 +17,15 @@ This section is a bit long, but worth the read. We recommend coffee, tea, and/or Dioxus is one the very few Rust libraries that provide a "Reactive Programming Model". The term "Reactive programming" is a classification of programming paradigm - much like functional or imperative programming. This is a very important distinction since it affects how we *think* about our code. -Reactive programming is programming model concerned with deriving computations from asynchronous data flow. Most reactive programs are comprised of a handful of datasources, intermediate computations, and a final result. +Reactive programming is a programming model concerned with deriving computations from asynchronous data flow. Most reactive programs are comprised of datasources, intermediate computations, and a final result. -We can consider the our rendered GUI to be the final result of reactive app and our datasources to include shared contexts and component properties. +We consider the rendered GUI to be the final result of our Dioxus apps. The datasources for our apps include local and global state. For example, the model presented in the figure below is comprised of two data sources: time and a constant. These values are passed through our computation graph to achieve a final result: `g`. ![Reactive Model](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Reactive_programming_glitches.svg/440px-Reactive_programming_glitches.svg.png) -Whenever our `seconds` variable changes, then we will reevaluate the computation for `t`. Because `g` relies on `t`, then we will also reevaluate its computation too. Notice that we would've reevaluated the computation for `g` even if `t` didn't change because `seconds` is used to calculate `g`. +Whenever our `seconds` variable changes, we will then reevaluate the computation for `t`. Because `g` relies on `t`, we will also reevaluate its computation too. Notice that we would've reevaluated the computation for `g` even if `t` didn't change because `seconds` is used to calculate `g`. However, if we somehow changed our constant from `1` to `2`, then we need to reevaluate `t`. If, for whatever reason, this change did not affect the result of `t`, then we wouldn't try to reevaluate `g`. @@ -51,7 +51,7 @@ fn compute_graph(constant: i32, seconds: i32) -> bool { ## How is Dioxus Reactive? -The Dioxus VirtualDom provides us a framework for reactive programming. When we build apps with dioxus, we need to feed it our own custom datasources. This can be either initial props or some values fetched from the network. We then pass this data through our app into components through properties. +The Dioxus VirtualDom provides us a framework for reactive programming. When we build apps with dioxus, we need to provide our own datasources. This can be either initial props or some values fetched from the network. We then pass this data through our app into components through properties. If we represented the reactive graph presented above in Dioxus, it would look very similar: @@ -85,7 +85,7 @@ With this app, we've defined three components. Our top-level component provides Now, whenever the `constant` changes, our `RenderT` component will be re-rendered. However, if `seconds` doesn't change, then we don't need to re-render `RenderG` because the input is the same. If `seconds` *does* change, then both RenderG and RenderT will be reevaluated. -Dioxus is "Reactive" because it provides this framework for us. All we need to do is write our own tiny units of computations and Dioxus figures out which components need to be reevaluated automatically. +Dioxus is "Reactive" because it provides this framework for us. All we need to do is write our own tiny units of computation and Dioxus figures out which components need to be reevaluated automatically. These extra checks and algorithms add some overhead, which is why you see projects like [Sycamore](http://sycamore-rs.netlify.app) and [SolidJS](http://solidjs.com) eliminating them altogether. Dioxus is *really* fast, so we're willing to exchange the added overhead for improved developer experience. @@ -142,11 +142,11 @@ A single component will be called multiple times, modifying its own internal sta With the `provide_context` and `consume_context` methods on `Scope`, we can share values to descendants without having to pass values through component props. This has the side-effect of making our datasources less obvious from a high-level perspective, but it makes our components more modular within the same codebase. -To make app-global state easier to reason about, Dioxus makes all values provided through `provide_context` immutable. This means any library built on top of `provide_context` needs to use interior mutability to modify share global state. +To make app-global state easier to reason about, Dioxus makes all values provided through `provide_context` immutable. This means any library built on top of `provide_context` needs to use interior mutability to modify shared global state. In these cases, App-Global state needs to manually track which components need to be re-generated. -To regenerate *any* component in your app, you can get a handle the Dioxus' internal scheduler through `schedule_update_any`: +To regenerate *any* component in your app, you can get a handle to the Dioxus' internal scheduler through `schedule_update_any`: ```rust let force_render = cx.schedule_update_any(); @@ -155,7 +155,7 @@ let force_render = cx.schedule_update_any(); force_render(ScopeId(0)); ``` -## What does it mean for a component to "re-render" +## What does it mean for a component to "re-render"? In our guides, we frequently use the phrase "re-render" to describe updates to our app. You'll often hear this paired with "preventing unnecessary re-renders." But what exactly does this mean? diff --git a/docs/guide/src/interactivity/index.md b/docs/guide/src/interactivity/index.md index b71a3e73d..a1f963bd3 100644 --- a/docs/guide/src/interactivity/index.md +++ b/docs/guide/src/interactivity/index.md @@ -112,11 +112,11 @@ For example, let's say we provide a button to generate a new post. Whenever the ```rust fn App(cx: Scope)-> Element { - let post = use_state(&cx, || PostData::new()); + let (post, set_post) = use_state(&cx, || PostData::new()); cx.render(rsx!{ button { - on_click: move |_| post.set(PostData::random()) + on_click: move |_| set_post(PostData::random()) "Generate a random post" } Post { props: &post } @@ -131,7 +131,6 @@ We'll dive much deeper into event listeners later. We can also update our state outside of event listeners with `futures` and `coroutines`. - `Futures` are Rust's version of promises that can execute asynchronous work by an efficient polling system. We can submit new futures to Dioxus either through `push_future` which returns a `TaskId` or with `spawn`. -- - `Coroutines` are asynchronous blocks of our component that have the ability to cleanly interact with values, hooks, and other data in the component. Since coroutines and Futures stick around between renders, the data in them must be valid for the `'static` lifetime. We must explicitly declare which values our task will rely on to avoid the `stale props` problem common in React. @@ -142,14 +141,14 @@ We can use tasks in our components to build a tiny stopwatch that ticks every se ```rust fn App(cx: Scope)-> Element { - let mut sec_elapsed = use_state(&cx, || 0); + let (elapsed, set_elapsed) = use_state(&cx, || 0); use_future(&cx, || { - to_owned![sec_elapsed]; // explicitly capture this hook for use in async + to_owned![set_elapsed]; // explicitly capture this hook for use in async async move { loop { TimeoutFuture::from_ms(1000).await; - sec_elapsed += 1; + set_elapsed.modify(|i| i + 1) } } }); @@ -162,8 +161,6 @@ Using asynchronous code can be difficult! This is just scratching the surface of ### How do I tell Dioxus that my state changed? -So far, we've only updated our state with `.set`. However, you might've noticed that we used `AddAssign` to increment the `sec_elapsed` value in our stopwatch example *without* calling set. This is because the `AddAssign` trait is implemented for `UseState` (the wrapper around our value returned from `use_state`). Under the hood, whenever you try to mutate our value through `UseState`, you're actually calling `.set` which informs Dioxus that _this_ component needs to be updated on the screen. - Whenever you inform Dioxus that the component needs to be updated, it will "render" your component again, storing the previous and current Elements in memory. Dioxus will automatically figure out the differences between the old and the new and generate a list of edits that the renderer needs to apply to change what's on the screen. This process is called "diffing": ![Diffing](../images/diffing.png) From 8b402f946afa163f1a1bdc17e38959a0e7131b8e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 29 Jan 2022 10:15:23 -0500 Subject: [PATCH 9/9] docs: updaet hooks --- docs/guide/src/SUMMARY.md | 5 +++++ docs/guide/src/interactivity/importanthooks.md | 2 ++ 2 files changed, 7 insertions(+) diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index d4990fc99..5023eab8e 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -14,6 +14,11 @@ - [Reusing, Importing, and Exporting](elements/exporting_components.md) - [Children and Attributes](elements/component_children.md) - [How Data Flows](elements/composing.md) +- [Thinking Reactively]() + - [Thinking Reactively]() + - [Thinking Reactively]() + - [Thinking Reactively]() + - [Thinking Reactively]() - [Adding Interactivity](interactivity/index.md) - [Hooks and Internal State](interactivity/hooks.md) - [UseState and UseRef](interactivity/importanthooks.md) diff --git a/docs/guide/src/interactivity/importanthooks.md b/docs/guide/src/interactivity/importanthooks.md index eff27b199..fb985ce90 100644 --- a/docs/guide/src/interactivity/importanthooks.md +++ b/docs/guide/src/interactivity/importanthooks.md @@ -7,6 +7,8 @@ Most components you will write in Dioxus will need to store state somehow. For l Both of these hooks are extremely powerful and flexible, so we've dedicated this section to understanding them properly. +> These two hooks are not the only way to store state. You can always build your own hooks! + ## Note on Hooks If you're struggling with errors due to usage in hooks, make sure you're following the rules of hooks: