diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 82b3fd4e2..fec34f914 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -180,14 +180,10 @@ impl ScopeContext { /// when a context already exists will swap the context out for the new one, which may not be what you want. pub fn provide_root_context(&self, context: T) -> T { with_runtime(|runtime| { - // Walk upwards until there is no more parent - and tada we have the root - let mut parent = runtime.get_context(self.scope_id()).unwrap(); - while let Some(next_parent) = parent.parent_id { - parent = runtime.get_context(next_parent).unwrap(); - } - debug_assert_eq!(parent.scope_id(), ScopeId(0)); - - parent.provide_context(context) + runtime + .get_context(ScopeId(0)) + .unwrap() + .provide_context(context) }) .expect("Runtime to exist") } diff --git a/packages/generational-box/src/lib.rs b/packages/generational-box/src/lib.rs index 29c7ebdb2..60bd41419 100644 --- a/packages/generational-box/src/lib.rs +++ b/packages/generational-box/src/lib.rs @@ -258,6 +258,7 @@ impl MemoryLocation { let old = self.data.borrow_mut().take(); #[cfg(any(debug_assertions, feature = "check_generation"))] if old.is_some() { + drop(old); let new_generation = self.generation.get() + 1; self.generation.set(new_generation); } diff --git a/packages/signals/Cargo.toml b/packages/signals/Cargo.toml index c145b850a..3df7d34ca 100644 --- a/packages/signals/Cargo.toml +++ b/packages/signals/Cargo.toml @@ -14,3 +14,4 @@ simple_logger = "4.2.0" [dev-dependencies] dioxus = { workspace = true } +dioxus-desktop = { workspace = true } diff --git a/packages/signals/README.md b/packages/signals/README.md index e65c613fe..68cb8d80a 100644 --- a/packages/signals/README.md +++ b/packages/signals/README.md @@ -54,3 +54,25 @@ fn Child(cx: Scope) -> Element { } } ``` + +Because subscriptions happen when you read from (not create) the data, you can provide signals through the normal context API: + +```rust +fn app(cx: Scope) -> Element { + // Because signal is never read in this component, this component will not rerun when the signal changes + use_context_provider(cx, || Signal::new(0)); + + render! { + Child {} + } +} + +fn Child(cx: Scope) -> Element { + let signal: Signal = *use_context(cx).unwrap(); + // This component does read from the signal, so when the signal changes it will rerun + render! { + "{signal}" + } +} + +``` diff --git a/packages/signals/examples/context.rs b/packages/signals/examples/context.rs new file mode 100644 index 000000000..ddd52e691 --- /dev/null +++ b/packages/signals/examples/context.rs @@ -0,0 +1,25 @@ +#![allow(non_snake_case)] + +use dioxus::prelude::*; +use dioxus_signals::{use_signal, Effect, Signal}; + +fn main() { + dioxus_desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + // Because signal is never read in this component, this component will not rerun when the signal changes + use_context_provider(cx, || Signal::new(0)); + + render! { + Child {} + } +} + +fn Child(cx: Scope) -> Element { + let signal: Signal = *use_context(cx).unwrap(); + // This component does read from the signal, so when the signal changes it will rerun + render! { + "{signal}" + } +} diff --git a/packages/signals/examples/split_subscriptions.rs b/packages/signals/examples/split_subscriptions.rs new file mode 100644 index 000000000..3eb60ed29 --- /dev/null +++ b/packages/signals/examples/split_subscriptions.rs @@ -0,0 +1,150 @@ +#![allow(non_snake_case)] + +use dioxus::prelude::*; +use dioxus_signals::Signal; + +fn main() { + dioxus_desktop::launch(app); +} + +#[derive(Clone, Copy, Default)] +struct ApplicationData { + first_data: Signal, + second_data: Signal, + many_signals: Signal>>, +} + +fn use_app_data(cx: Scope) -> ApplicationData { + *use_context(cx).unwrap() +} + +fn app(cx: Scope) -> Element { + use_context_provider(cx, ApplicationData::default); + + render! { + div { + ReadsFirst {} + } + div { + ReadsSecond {} + } + div { + ReadsManySignals {} + } + } +} + +fn ReadsFirst(cx: Scope) -> Element { + println!("running first"); + let data = use_app_data(cx); + + render! { + button { + onclick: move |_| { + *data.first_data.write() += 1; + }, + "Increase" + } + button { + onclick: move |_| { + *data.first_data.write() -= 1; + }, + "Decrease" + } + button { + onclick: move |_| { + *data.first_data.write() = 0; + }, + "Reset" + } + "{data.first_data}" + } +} + +fn ReadsSecond(cx: Scope) -> Element { + println!("running second"); + let data = use_app_data(cx); + + render! { + button { + onclick: move |_| { + *data.second_data.write() += 1; + }, + "Increase" + } + button { + onclick: move |_| { + *data.second_data.write() -= 1; + }, + "Decrease" + } + button { + onclick: move |_| { + *data.second_data.write() = 0; + }, + "Reset" + } + "{data.second_data}" + } +} + +fn ReadsManySignals(cx: Scope) -> Element { + println!("running many signals"); + let data = use_app_data(cx); + + render! { + button { + onclick: move |_| { + data.many_signals.write().push(Signal::new(0)); + }, + "Create" + } + button { + onclick: move |_| { + data.many_signals.write().pop(); + }, + "Destroy" + } + button { + onclick: move |_| { + if let Some(first) = data.many_signals.read().get(0) { + *first.write() += 1; + } + }, + "Increase First Item" + } + for signal in data.many_signals { + Child { + count: signal, + } + } + } +} + +#[derive(Props, PartialEq)] +struct ChildProps { + count: Signal, +} + +fn Child(cx: Scope) -> Element { + println!("running child"); + let count = cx.props.count; + + render! { + div { + "Child: {count}" + button { + onclick: move |_| { + *count.write() += 1; + }, + "Increase" + } + button { + onclick: move |_| { + *count.write() -= 1; + }, + "Decrease" + } + } + } +}