diff --git a/docs/book/.gitignore b/docs/book/.gitignore new file mode 100644 index 000000000..e9c072897 --- /dev/null +++ b/docs/book/.gitignore @@ -0,0 +1 @@ +book \ No newline at end of file diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md index b85db0808..0cfc4d9f9 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -11,8 +11,8 @@ - [Control Flow](./view/06_control_flow.md) - [Error Handling](./view/07_errors.md) - [Parent-Child Communication](./view/08_parent_child.md) - - [Passing Children to Components]() -- [Interlude: Reactivity and Functions]() + - [Passing Children to Components](./view/09_component_children.md) +- [Interlude: Reactivity and Functions](interlude_functions.md) - [Testing]() - [Interlude: Styling — CSS, Tailwind, Style.rs, and more]() - [Async]() diff --git a/docs/book/src/interlude_functions.md b/docs/book/src/interlude_functions.md new file mode 100644 index 000000000..962c932c7 --- /dev/null +++ b/docs/book/src/interlude_functions.md @@ -0,0 +1,76 @@ +# Interlude: Reactivity and Functions + +One of our core contributors said to me recently: “I never used closures this often +until I started using Leptos.” And it’s true. Closures are at the heart of any Leptos +application. It sometimes looks a little silly: + +```rust +// a signal holds a value, and can be updated +let (count, set_count) = create_signal(cx, 0); + +// a derived signal is a function that accesses other signals +let double_count = move || count() * 2; +let count_is_odd = move || count() & 1 == 1; +let text = move || if count_is_odd() { + "odd" +} else { + "even" +}; + +// an effect automatically tracks the signals it depends on +// and re-runs when they change +create_effect(cx, move |_| { + log!("text = {}", text()); +}); + +view! { cx, +

{move || text().to_uppercase()}

+} +``` + +Closures, closures everywhere! + +But why? + +## Functions and UI Frameworks + +Functions are at the heart of every UI framework. And this makes perfect sense. Creating a user interface is basically divided into two phases: + +1. initial rendering +2. updates + +In a web framework, the framework does some kind of initial rendering. Then it hands control back over to the browser. When certain events fire (like a mouse click) or asynchronous tasks finish (like an HTTP request finishing), the browser wakes the framework back up to update something. The framework runs some kind of code to update your user interface, and goes back asleep until the browser wakes it up again. + +The key phrase here is “runs some kind of code.” The natural way to “run some kind of code” at an arbitrary point in time—in Rust or in any other programming language—is to call a function. And in fact every UI framework is based on rerunning some kind of function over and over: + +1. virtual DOM (VDOM) frameworks like React, Yew, or Dioxus rerun a component or render function over and over, to generate a virtual DOM tree that can be reconciled with the previous result to patch the DOM +2. compiled frameworks like Angular and Svelte divide your component templates into “create” and “update” functions, rerunning the update function when they detect a change to the component’s state +3. in fine-grained reactive frameworks like SolidJS, Sycamore, or Leptos, _you_ define the functions that re-run + +That’s what all our components are doing. + +Take our typical `` example in its simplest form: + +```rust +#[component] +pub fn SimpleCounter(cx: Scope) -> impl IntoView { + let (value, set_value) = create_signal(cx, 0); + + let increment = move |_| set_value.update(|value| *value += 1); + + view! { cx, + + } +} +``` + +The `SimpleCounter` function itself runs once. The `value` signal is created once. The framework hands off the `increment` function to the browser as an event listener. When you click the button, the browser calls `increment`, which updates `value` via `set_value`. And that updates the single text node represented in our view by `{value}`. + +Closures are key to reactivity. They provide the framework with the ability to re-run the smallest possible unit of your application in responsive to a change. + +So remember two things: + +1. Your component function is a setup function, not a render function: it only runs once. +2. For values in your view template to be reactive, they must be functions: either signals (which implement the `Fn` traits) or closures. diff --git a/docs/book/src/view/06_control_flow.md b/docs/book/src/view/06_control_flow.md index a2245ad26..1b193ae38 100644 --- a/docs/book/src/view/06_control_flow.md +++ b/docs/book/src/view/06_control_flow.md @@ -187,6 +187,8 @@ This rerenders `` five times, then `` infinitely. If they’re loading resources, creating signals, or even just creating DOM nodes, this is unnecessary work. +### `` + The [``](https://docs.rs/leptos/latest/leptos/fn.Show.html) component is the answer. You pass it a `when` condition function, a `fallback` to be shown if the `when` function returns `false`, and children to be rendered if `when` is `true`. diff --git a/docs/book/src/view/09_component_children.md b/docs/book/src/view/09_component_children.md new file mode 100644 index 000000000..2d67050c1 --- /dev/null +++ b/docs/book/src/view/09_component_children.md @@ -0,0 +1,124 @@ +# Component Children + +It’s pretty common to want to pass children into a component, just as you can pass +children into an HTML element. For example, imagine I have a `` component +that enhances an HTML `
`. I need some way to pass all its inputs. + +```rust +view! { cx, + +
+ +
+ +
+} +``` + +How can you do this in Leptos? There are basically two ways to pass components to +other components: + +1. **render props**: properties that are functions that return a view +2. the **`children`** prop: a special component property that includes anything + you pass as a child to the component. + +In fact, you’ve already seen these both in action in the [``](/view/06_control_flow.html#show) component: + +```rust +view! { cx, + 5 + // `fallback` is a "render prop": a function that returns a view + fallback=|cx| view! { cx, } + > + // `` (and anything else here) + // will be given to the `children` prop + + +} +``` + +Let’s define a component that takes some children and a render prop. + +```rust +#[component] +pub fn TakesChildren( + cx: Scope, + /// Takes a function (type F) that returns anything that can be + /// converted into a View (type IV) + render_prop: F, + /// `children` takes the `Children` type + children: Children, +) -> impl IntoView +where + F: Fn() -> IV, + IV: IntoView, +{ + view! { cx, +

"Render Prop"

+ {render_prop()} + +

"Children"

+ {children(cx)} + } +} +``` + +`render_prop` and `children` are both functions, so we can call them to generate +the appropriate views. `children`, in particular, is an alias for +`Box Fragment>`. (Aren't you glad we named it `Children` instead?) + +> If you need a `Fn` or `FnMut` here because you need to call `children` more than once, +> we also provide `ChildrenFn` and `ChildrenMut` aliases. + +We can use the component like this: + +```rust +view! { cx, + "Hi, there!"

}> + // these get passed to `children` + "Some text" + "A span" +
+} +``` + +## Manipulating Children + +The [`Fragment`](https://docs.rs/leptos/latest/leptos/struct.Fragment.html) type is +basically a way of wrapping a `Vec`. You can insert it anywhere into your view. + +But you can also access those inner views directly to manipulate them. For example, here’s +a component that takes its children and turns them into an unordered list. + +```rust +#[component] +pub fn WrapsChildren(cx: Scope, children: Children) -> impl IntoView { + // Fragment has `nodes` field that contains a Vec + let children = children(cx) + .nodes + .into_iter() + .map(|child| view! { cx,
  • {child}
  • }) + .collect::>(); + + view! { cx, +
      {children}
    + } +} +``` + +Calling it like this will create a list: + +```rust +view! { cx, + + "A" + "B" + "C" + +} +```