فهرست منبع

Proofread chapter 6

Reinis Mazeiks 3 سال پیش
والد
کامیت
ebc768932d

+ 0 - 5
docs/guide/src/SUMMARY.md

@@ -14,11 +14,6 @@
   - [Reusing, Importing, and Exporting](components/exporting_components.md)
   - [Children and Attributes](components/component_children.md)
   - [How Data Flows](components/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) 

+ 0 - 2
docs/guide/src/components/component_children.md

@@ -215,5 +215,3 @@ In this chapter, we learned:
 - How to convert `listeners` into `EventHandlers` for components
 - How to extend any node with custom attributes and children
 
-Next chapter, we'll talk about conditionally rendering parts of your user interface.
-

+ 1 - 1
docs/guide/src/components/composing.md

@@ -1,6 +1,6 @@
 # Thinking in React
 
-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*.
+We've finally reached the point in our tutorial where we can talk about the theory of Reactive interfaces. We've talked about defining a declarative view, but not about the aspects that make our code *reactive*.
 
 Understanding the theory of reactive programming is essential to making sense of Dioxus and writing effective, performant UIs.
 

+ 2 - 12
docs/guide/src/components/exporting_components.md

@@ -1,4 +1,3 @@
-
 # Reusing, Importing, and Exporting Components
 
 As your application grows in size, you'll want to start breaking your UI into components and, eventually, different files. This is a great idea to encapsulate functionality of your UI and scale your team.
@@ -67,6 +66,8 @@ fn ActionCard(Scope<ActionCardProps>) -> 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
+// src/post/mod.rs
+
 use dioxus::prelude::*;
 
 #[derive(PartialEq, Props)]
@@ -289,14 +290,3 @@ use dioxus::prelude::*;
 pub struct ActionCardProps {}
 pub fn ActionCard(Scope<ActionCardProps>) -> Element {}
 ```
-
-## Moving forward
-
-Next chapter, we'll start to add use code to hide and show Elements with conditional rendering.
-
-For more reading on components:
-
-- [Components in depth]()
-- [Lifecycles]()
-- [The Context object]()
-- [Optional Prop fields]()

+ 6 - 169
docs/guide/src/components/index.md

@@ -1,23 +1,22 @@
 # Introduction to Components
 
-In the previous chapter, we learned about Elements and how they can be composed to create a basic User Interface. In this chapter, we'll learn how to group Elements together to form Components.
+In the previous chapter, we learned about Elements and how they can be composed to create a basic user interface. Now, we'll learn how to group Elements together to form Components. We'll cover:
 
-In this chapter, we'll learn:
 - What makes a Component
 - How to model a component and its properties in Dioxus
 - How to "think declaratively"
 
 ## What is a component?
 
-In short, a component is a special function that takes input properties and outputs an Element. Typically, Components serve a single purpose: group functionality of a User Interface. Much like a function encapsulates some specific computation task, a Component encapsulates some specific rendering task.
+In short, a component is a special function that takes input properties and outputs an Element. Much like a function encapsulates some specific computation task, a Component encapsulates some specific rendering task – typically, rendering an isolated part of the user interface.
 
-### Learning through prior art
+### Real-world example
 
-Let's take a look at a post on r/rust and see if we can sketch out a component representation.
+Take a look at a post on Reddit:
 
 ![Reddit Post](../images/reddit_post.png)
 
-This component has a bunch of important information:
+We can build this post as a component, consisting of multiple subcomponents. It has several inputs:
 
 - The score
 - The number of comments
@@ -62,166 +61,4 @@ If we included all this functionality in one `rsx!` call it would be huge! Inste
 - **MetaCard**: Original Poster, Time Submitted
 - **ActionCard**: View comments, Share, Save, Hide, Give award, Report, Crosspost
 
-### Modeling with Dioxus
-
-We can start by sketching out the Element hierarchy using Dioxus. In general, our "Post" component will be comprised of the four sub-components listed above. First, let's define our `Post` component.
-
-Unlike normal functions, Dioxus components must explicitly define a single struct to contain all the inputs. These are commonly called "Properties" (props). Our component will be a combination of these properties and a function to render them.
-
-Our props must implement the `Props` trait and - if the component does not borrow any data - `PartialEq`. Both of these can be done automatically through derive macros:
-```rust
-#[derive(Props, PartialEq)]
-struct PostProps {
-    id: Uuid,
-    score: i32,
-    comment_count: u32,
-    post_time: Instant,
-    url: String,
-    title: String,
-    original_poster: String
-}
-```
-
-And our render function:
-```rust
-fn Post(cx: Scope<PostProps>) -> Element {
-    cx.render(rsx!{
-        div { class: "post-container"
-            VoteButton {
-                score: cx.props.score,
-            }
-            TitleCard {
-                title: cx.props.title,
-                url: cx.props.url,
-            }
-            MetaCard {
-                original_poster: cx.props.original_poster,
-                post_time: cx.props.post_time,
-            }
-            ActionCard {
-                post_id: cx.props.id
-            }
-        }
-    })
-}
-```
-
-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.
-
-Most of your Components will look exactly like this: a Props struct and a render function. Every component must take a `Scope` generic over some `Props` and return an `Element`.
-
-As covered before, we'll build our User Interface with the `rsx!` macro and HTML tags. However, with components, we must actually "render" our HTML markup. Calling `cx.render` converts our "lazy" `rsx!` structure into an `Element`.
-
-```rust
-#[derive(PartialEq, Props)]
-struct VoteButtonProps {
-    score: i32
-}
-
-fn VoteButton(cx: Scope<VoteButtonProps>) -> Element {
-    cx.render(rsx!{
-        div { class: "votebutton"
-            div { class: "arrow up" }
-            div { class: "score", "{cx.props.score}"}
-            div { class: "arrow down" }
-        }
-    })
-}
-```
-
-## Borrowing
-
-You can avoid clones by using borrowed component syntax. For example, let's say we passed the `TitleCard` title as an `&str` instead of `String`. In JavaScript, the string would be copied by reference - none of the contents would be copied, but rather the reference to the string's contents are copied. In Rust, this would be similar to calling `clone` on `Rc<str>`.
-
-Because we're working in Rust, we can choose to either use `Rc<str>`, clone `Title` on every re-render of `Post`, or simply borrow it. In most cases, you'll just want to let `Title` be cloned.
-
-To enable borrowed values for your component, we need to add a lifetime to let the Rust compiler know that the output `Element` borrows from the component's props.
-
-```rust
-#[derive(Props)]
-struct TitleCardProps<'a> {
-    title: &'a str,
-}
-
-fn TitleCard<'a>(cx: Scope<'a, TitleCardProps<'a>>) -> Element {
-    cx.render(rsx!{
-        h1 { "{cx.props.title}" }
-    })
-}
-```
-
-For users of React: Dioxus knows *not* to memoize components that borrow property fields. By default, every component in Dioxus is memoized. This can be disabled by the presence of a non-`'static` borrow.
-
-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<TitleCardProps>) -> 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.
-
-Have you ever wondered how the `useState()` call works in React without a `this` object to actually store the state?
-
-React uses global variables to store this information. Global mutable variables must be carefully managed and are broadly discouraged in Rust programs.
-
-```javascript
-function Component(props) {
-    let [state, set_state] = useState(10);
-}
-```
-
-
-Because Dioxus needs to work with the rules of Rust it uses the `Scope` object to maintain some internal bookkeeping. That's what the `Scope` object is: a place for the component to store state, manage listeners, and allocate elements. Advanced users of Dioxus will want to learn how to properly leverage the `Scope` object to build robust and performant extensions for Dioxus.
-
-```rust
-fn Post(cx: Scope<PostProps>) -> Element {
-    cx.render(rsx!("hello"))
-}
-```
-## Moving forward
-
-Next chapter, we'll talk about composing Elements and Components across files to build a larger Dioxus App.
-
-For more references on components, make sure to check out:
-
-- [Components in depth]()
-- [Lifecycles]()
-- [The Scope object]()
-- [Optional Prop fields]()
-
+In this chapter, we'll learn how to define our own components.

+ 52 - 83
docs/guide/src/components/propsmacro.md

@@ -1,81 +1,77 @@
 # Component Properties
+Dioxus components are functions that accept Props as input and output an Element. In fact, the `App` function you saw in the previous chapter was a component with no Props! Most components, however, will need to take some Props to render something useful – so, in this section, we'll learn about props:
 
-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
+- Deriving the Props trait
 - Memoization through PartialEq
 - Optional fields on props
 - The inline_props macro    
 
+## Props
+All properties that your components take must implement the `Props` trait. We can derive this trait on our own structs to define a Component's props.
 
+> Dioxus `Props` 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.
 
-## 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 `()`.
+There are 2 flavors of Props: owned and borrowed.
 
-```rust
-// this scope
-Scope<()> 
+- All Owned Props must implement `PartialEq`
+- Borrowed props [borrow](https://doc.rust-lang.org/beta/rust-by-example/scope/borrow.html) values from the parent Component
 
-// 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:
+### Owned Props
 
-```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:
+Owned Props are very simple – they don't borrow anything. Example:
 ```rust
 #[derive(Props, PartialEq)]
 struct MyProps {
     name: String
 }
-```
 
-For a borrowed example:
-```rust
-#[derive(Props)]
-struct MyProps<'a> {
-    name: &'a str
+fn Demo(cx: Scope<MyProps>) -> Element {
+    todo!()
 }
 ```
 
-Then, to use these props in our component, we simply swap out the generic parameter on scope.
+> The simplest Owned Props you can have is `()` - or no value at all. This is what the `App` Component takes as props. `Scope` accepts a generic for the Props which defaults to `()`.
+> 
+> ```rust
+>// this scope
+>Scope<()> 
+>
+>// is the same as this scope
+>Scope
+>```
 
-For owned props, we just drop it in:
+### Borrowed Props
 
-```rust
-fn Demo(cx: Scope<MyProps>) -> Element {
-    todo!()
-}
-```
+Owning a string works well as long as you only need it in one place. But what if we need to pass a String from a `Post` Component to a `TitleCard` subcomponent? A naive solution might be to [`.clone()`](https://doc.rust-lang.org/std/clone/trait.Clone.html) the String, creating a copy of it – but this would be inefficient, especially for larger Strings.
+
+Rust allows for something more efficient – borrowing the String as a `&str`. Instead of creating a copy, this will give us a reference to the original String – this is what Borrowed Props are for!
 
-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:
+However, if we create a reference a String, Rust will require us to show that the String will not go away while we're using the reference. Otherwise, if we referenced something that doesn't exist, Bad Things could happen. To prevent this, Rust asks us to define a lifetime for the reference:
 
 ```rust
-fn Demo<'a>(cx: Scope<'a, MyProps<'a>>) -> Element {
-    todo!()
+#[derive(Props)]
+struct TitleCardProps<'a> {
+    title: &'a str,
 }
-```
 
-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.
+fn TitleCard<'a>(cx: Scope<'a, TitleCardProps<'a>>) -> Element {
+    cx.render(rsx!{
+        h1 { "{cx.props.title}" }
+    })
+}
+```
 
+This lifetime `'a` tells the compiler that as long as `title` exists, the String it was created from must also exist. Dioxus will happily accept such a component.
 
 ## 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.
+Dioxus uses Memoization for a more efficient user interface. 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 affect the output, then we don't need to re-render the component, saving time!
 
 For example, let's say we have a component that has two children:
 
 ```rust
 fn Demo(cx: Scope) -> Element {
+    // don't worry about these 2, we'll cover them later
     let name = use_state(&cx, || String::from("bob"));
     let age = use_state(&cx, || 21);
 
@@ -88,35 +84,15 @@ fn Demo(cx: Scope) -> Element {
 
 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 memoizes owned components. It uses `PartialEq` to determine if a component needs rerendering, or if it has stayed the same. This is why you must derive PartialEq!
 
-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.
+> This 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.
 
+Borrowed Props cannot be safely memoized. However, this is not a problem – Dioxus relies on the memoization of their parents to determine if they need to be rerendered.
 
-However, for components that borrow values from their parents, we cannot safely memoize them.
+## Optional Props
 
-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.
+You can easily create optional fields by attaching the `optional` modifier to a field:
 
 ```rust
 #[derive(Props, PartialEq)]
@@ -128,7 +104,7 @@ struct MyProps {
 }
 
 fn Demo(cx: MyProps) -> Element {
-    ...
+    todo!()
 }
 ```
 
@@ -147,21 +123,16 @@ The `optional` modifier is a combination of two separate modifiers: `default` an
 
 - `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`
+- `optional` - alias for `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
 
+So far, every Component function we've seen had a corresponding ComponentProps struct to pass in props. This was quite verbose... Wouldn't it be nice to have props as simple function arguments? Then we wouldn't need to define a Props struct, and instead of typing `cx.props.whatever`, we could just use `whatever` directly!
 
-
-## 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:
+`inline_props` allows you to do just that. Instead of typing the "full" version:
 
 ```rust
 #[derive(Props, PartialEq)]
@@ -173,10 +144,10 @@ fn TitleCard(cx: Scope<TitleCardProps>) -> Element {
     cx.render(rsx!{
         h1 { "{cx.props.title}" }
     })
-}   
+}
 ```
 
-to:
+...you can define a function that accepts props as arguments. Then, just annotate it with `#[inline_props]`, and the macro will turn it into a regular Component for you:
 
 ```rust
 #[inline_props]
@@ -184,9 +155,7 @@ 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.
+> While the new Component is shorter and easier to read, this macro should not be used by library authors since you have less control over Prop documentation.

+ 25 - 1
docs/guide/src/interactivity/index.md

@@ -1,6 +1,6 @@
 # Adding Interactivity
 
-So far, we've learned how to describe the structure and properties of our user interfaces. Unfortunately, they're static and quite a bit uninteresting. In this chapter, we're going to learn how to add interactivity through events, state, and tasks.
+So far, we've learned how to describe the structure and properties of our user interfaces. Unfortunately, they're static and quite uninteresting. In this chapter, we're going to learn how to add interactivity through events, state, and tasks.
 
 ## Primer on interactivity
 
@@ -179,6 +179,30 @@ In general, Dioxus should be plenty fast for most use cases. However, there are
 
 Don't worry - Dioxus is fast. But, if your app needs *extreme performance*, then take a look at the `Performance Tuning` in the `Advanced Guides` book.
 
+## 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.
+
+Have you ever wondered how the `useState()` call works in React without a `this` object to actually store the state?
+
+```javascript
+// in React:
+function Component(props) {
+    // This state persists between component renders, but where does it live?
+    let [state, set_state] = useState(10);
+}
+```
+
+React uses global variables to store this information. However, global mutable variables must be carefully managed and are broadly discouraged in Rust programs. Because Dioxus needs to work with the rules of Rust it uses the `Scope` rather than a global state object to maintain some internal bookkeeping.
+
+That's what the `Scope` object is: a place for the Component to store state, manage listeners, and allocate elements. Advanced users of Dioxus will want to learn how to properly leverage the `Scope` object to build robust and performant extensions for Dioxus.
+
+```rust
+fn Post(cx: Scope<PostProps>) -> Element {
+    cx.render(rsx!("hello"))
+}
+```
+
 ## Moving On
 
 This overview was a lot of information - but it doesn't tell you everything!