Forráskód Böngészése

Merge pull request #1193 from DioxusLabs/jk/re-add-root-context

Re-add provide_root_context for lazy singleton initialization
Jonathan Kelley 1 éve
szülő
commit
7ab9115067

+ 1 - 4
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 {

+ 6 - 4
packages/core-macro/tests/ifmt.rs

@@ -1,5 +1,3 @@
-use std::borrow::Borrow;
-
 use dioxus_core_macro::*;
 
 #[test]
@@ -20,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
@@ -30,3 +28,7 @@ fn formatting_compiles() {
         format!("{x:?} {x:?}")
     );
 }
+
+fn blah(hi: &(i32, i32)) -> String {
+    format_args_f!("{hi.0} {hi.1}").to_string()
+}

+ 19 - 0
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<T: 'static + Clone>(&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<Output = ()> + 'static) -> TaskId {
         let id = self.tasks.spawn(self.id, fut);

+ 3 - 0
packages/hooks/src/lib.rs

@@ -79,3 +79,6 @@ pub use usecallback::*;
 
 mod usememo;
 pub use usememo::*;
+
+mod userootcontext;
+pub use userootcontext::*;

+ 9 - 0
packages/hooks/src/userootcontext.rs

@@ -0,0 +1,9 @@
+use dioxus_core::ScopeState;
+
+///
+pub fn use_root_context<T: 'static + Clone>(cx: &ScopeState, new: impl FnOnce() -> T) -> &T {
+    cx.use_hook(|| {
+        cx.consume_context::<T>()
+            .unwrap_or_else(|| cx.provide_root_context(new()))
+    })
+}

+ 8 - 2
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<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T> {
     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());