From 2d8841cf70eaaf5990507c889d74e650c43ad585 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 14 Jul 2023 12:52:49 -0700 Subject: [PATCH 1/3] Add lazy contexts to signals --- examples/signals.rs | 5 +---- packages/core/src/scopes.rs | 19 +++++++++++++++++++ packages/hooks/src/lib.rs | 3 +++ packages/hooks/src/userootcontext.rs | 9 +++++++++ packages/signals/src/lib.rs | 10 ++++++++-- 5 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 packages/hooks/src/userootcontext.rs diff --git a/examples/signals.rs b/examples/signals.rs index 988b9af41..725577206 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -1,5 +1,4 @@ use dioxus::prelude::*; -use dioxus_signals::{use_init_signal_rt, use_signal}; use std::time::Duration; fn main() { @@ -7,9 +6,7 @@ fn main() { } fn app(cx: Scope) -> Element { - use_init_signal_rt(cx); - - let mut count = use_signal(cx, || 0); + let mut count = dioxus_signals::use_signal(cx, || 0); use_future!(cx, || async move { loop { diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 2b9f32f8d..ff94c456d 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -418,6 +418,25 @@ impl<'src> ScopeState { value } + /// Provide a context to the root and then consume it + /// + /// This is intended for "global" state management solutions that would rather be implicit for the entire app. + /// Things like signal runtimes and routers are examples of "singletons" that would benefit from lazy initialization. + /// + /// Note that you should be checking if the context existed before trying to provide a new one. Providing a context + /// 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 { + let mut parent = self; + + // Walk upwards until there is no more parent - and tada we have the root + while let Some(next_parent) = parent.parent { + parent = unsafe { &*next_parent }; + debug_assert_eq!(parent.scope_id(), ScopeId(0)); + } + + parent.provide_context(context) + } + /// Pushes the future onto the poll queue to be polled after the component renders. pub fn push_future(&self, fut: impl Future + 'static) -> TaskId { let id = self.tasks.spawn(self.id, fut); diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index 58b6c82f5..a2fb23ed1 100644 --- a/packages/hooks/src/lib.rs +++ b/packages/hooks/src/lib.rs @@ -79,3 +79,6 @@ pub use usecallback::*; mod usememo; pub use usememo::*; + +mod userootcontext; +pub use userootcontext::*; diff --git a/packages/hooks/src/userootcontext.rs b/packages/hooks/src/userootcontext.rs new file mode 100644 index 000000000..9b2fab719 --- /dev/null +++ b/packages/hooks/src/userootcontext.rs @@ -0,0 +1,9 @@ +use dioxus_core::ScopeState; + +/// +pub fn use_root_context(cx: &ScopeState, new: impl FnOnce() -> T) -> &T { + cx.use_hook(|| { + cx.consume_context::() + .unwrap_or_else(|| cx.provide_root_context(new())) + }) +} diff --git a/packages/signals/src/lib.rs b/packages/signals/src/lib.rs index e4b499ed8..4e950e0dc 100644 --- a/packages/signals/src/lib.rs +++ b/packages/signals/src/lib.rs @@ -10,16 +10,22 @@ mod rt; use dioxus_core::ScopeState; pub use rt::*; +use crate::rt::claim_rt; + pub fn use_init_signal_rt(cx: &ScopeState) { cx.use_hook(|| { - let rt = crate::rt::claim_rt(cx.schedule_update_any()); + let rt = claim_rt(cx.schedule_update_any()); cx.provide_context(rt); }); } pub fn use_signal(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal { cx.use_hook(|| { - let rt: &'static SignalRt = cx.consume_context().unwrap(); + let rt: &'static SignalRt = match cx.consume_context() { + Some(rt) => rt, + None => cx.provide_context(claim_rt(cx.schedule_update_any())), + }; + let id = rt.init(f()); rt.subscribe(id, cx.scope_id()); From 3b3020edd2cf2dc3ca226973e858694485935f30 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 14 Jul 2023 13:13:33 -0700 Subject: [PATCH 2/3] Make clippy ignore dumb lint --- packages/core-macro/tests/ifmt.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core-macro/tests/ifmt.rs b/packages/core-macro/tests/ifmt.rs index 3e03d37a9..195a8651c 100644 --- a/packages/core-macro/tests/ifmt.rs +++ b/packages/core-macro/tests/ifmt.rs @@ -1,3 +1,5 @@ +#![allow(clippy::all)] + use std::borrow::Borrow; use dioxus_core_macro::*; From f62af4057fe3b01f164e35d2dd160ac2b12f2643 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 14 Jul 2023 13:22:08 -0700 Subject: [PATCH 3/3] Fix clippy lint --- packages/core-macro/tests/ifmt.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core-macro/tests/ifmt.rs b/packages/core-macro/tests/ifmt.rs index 195a8651c..2883b8fd6 100644 --- a/packages/core-macro/tests/ifmt.rs +++ b/packages/core-macro/tests/ifmt.rs @@ -1,7 +1,3 @@ -#![allow(clippy::all)] - -use std::borrow::Borrow; - use dioxus_core_macro::*; #[test] @@ -22,8 +18,8 @@ fn formatting_compiles() { // function calls in formatings work assert_eq!( - format_args_f!("{x.borrow():?}").to_string(), - format!("{:?}", x.borrow()) + format_args_f!("{blah(&x):?}").to_string(), + format!("{:?}", blah(&x)) ); // allows duplicate format args @@ -32,3 +28,7 @@ fn formatting_compiles() { format!("{x:?} {x:?}") ); } + +fn blah(hi: &(i32, i32)) -> String { + format_args_f!("{hi.0} {hi.1}").to_string() +}