From 20d7685a740093ec0df7a12208f7a6055e14856d Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Thu, 28 Mar 2024 16:42:44 -0400 Subject: [PATCH] add appendix on the component/ownership life cycle (#87) --- src/SUMMARY.md | 1 + src/appendix_life_cycle.md | 175 +++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 src/appendix_life_cycle.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 683473b..86996ff 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -56,4 +56,5 @@ - [Guide: Islands](./islands.md) - [Appendix: How Does the Reactive System Work?](./appendix_reactive_graph.md) +- [Appendix: The Life Cycle of a Signal](./appendix_life_cycle.md) diff --git a/src/appendix_life_cycle.md b/src/appendix_life_cycle.md new file mode 100644 index 0000000..3ad33ce --- /dev/null +++ b/src/appendix_life_cycle.md @@ -0,0 +1,175 @@ +# Appendix: The Life Cycle of a Signal + +Three questions commonly arise at the intermediate level when using Leptos: +1. How can I connect to the component lifecycle, running some code when a component mounts or unmounts? +2. How do I know when signals are disposed, and why do I get an occasional panic when trying to access a disposed signal? +3. How is it possible that signals are `Copy` and can be moved into closures and other structures without being explicitly cloned? + +The answers to these three questions are closely inter-related, and are each somewhat complicated. This appendix will try to give you the context for understanding the answers, so that you can reason correctly about your application's code and how it runs. + +## The Component Tree vs. The Decision Tree + +Consider the following simple Leptos app: + +```rust +use leptos::logging::log; +use leptos::*; + +#[component] +pub fn App() -> impl IntoView { + let (count, set_count) = create_signal(0); + + view! { + + {move || if count() % 2 == 0 { + view! {

"Even numbers are fine."

}.into_view() + } else { + view! { }.into_view() + }} + } +} + +#[component] +pub fn InnerComponent(count: ReadSignal) -> impl IntoView { + create_effect(move |_| { + log!("count is odd and is {}", count()); + }); + + view! { + +

{count}

+ } +} + +#[component] +pub fn OddDuck() -> impl IntoView { + view! { +

"You're an odd duck."

+ } +} +``` + +All it does is show a counter button, and then one message if it's even, and a different message if it's odd. If it's odd, it also logs the values in the console. + +One way to map out this simple application would be to draw a tree of nested components: +``` +App +|_ InnerComponent + |_ OddDuck +``` + +Another way would be to draw the tree of decision points: +``` +root +|_ is count even? + |_ yes + |_ no +``` + +If you combine the two together, you'll notice that they don't map onto one another perfectly. The decision tree slices the view we created in `InnerComponent` into three pieces, and combines part of `InnerComponent` with the `OddDuck` component: +``` +DECISION COMPONENT DATA SIDE EFFECTS +root (count) render