1
0
Эх сурвалжийг харах

add a way to add manual dependency tuple to memos and effects

Evan Almloff 1 жил өмнө
parent
commit
0951a389f7

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

@@ -53,8 +53,6 @@ macro_rules! to_owned {
         $(to_owned![$($rest)*])?
     };
 }
-mod dependency;
-pub use dependency::*;
 
 mod use_callback;
 pub use use_callback::*;

+ 61 - 3
packages/hooks/src/use_effect.rs

@@ -2,6 +2,9 @@ use dioxus_core::prelude::*;
 use dioxus_signals::ReactiveContext;
 use futures_util::StreamExt;
 
+use crate::use_signal;
+use dioxus_signals::*;
+
 /// `use_effect` will subscribe to any changes in the signal values it captures
 /// effects will always run after first mount and then whenever the signal values change
 /// If the use_effect call was skipped due to an early return, the effect will no longer activate.
@@ -19,15 +22,15 @@ use futures_util::StreamExt;
 /// }
 /// ```
 #[track_caller]
-pub fn use_effect(mut callback: impl FnMut() + 'static) {
+pub fn use_effect(mut callback: impl FnMut() + 'static) -> Effect {
     // let mut run_effect = use_hook(|| CopyValue::new(true));
     // use_hook_did_run(move |did_run| run_effect.set(did_run));
 
     let location = std::panic::Location::caller();
 
     use_hook(|| {
+        let (rc, mut changed) = ReactiveContext::new_with_origin(location);
         spawn(async move {
-            let (rc, mut changed) = ReactiveContext::new_with_origin(location);
             loop {
                 // Run the effect
                 rc.run_in(&mut callback);
@@ -39,5 +42,60 @@ pub fn use_effect(mut callback: impl FnMut() + 'static) {
                 wait_for_next_render().await;
             }
         });
-    });
+        Effect { rc }
+    })
+}
+
+/// A handle to an effect.
+#[derive(Clone, Copy)]
+pub struct Effect {
+    rc: ReactiveContext,
+}
+
+impl Effect {
+    /// Adds an explicit dependency to the effect. If the dependency changes, the effect's closure will rerun.
+    ///
+    /// Signals will automatically be added as dependencies, so you don't need to call this method for them.
+    ///
+    /// NOTE: You must follow the rules of hooks when calling this method.
+    ///
+    /// ```rust
+    /// # use dioxus::prelude::*;
+    /// # async fn sleep(delay: u32) {}
+    ///
+    /// #[component]
+    /// fn Comp(delay: u32) -> Element {
+    ///     // Because the resource subscribes to `delay` by adding it as a dependency, the effect's closure will rerun every time `delay` changes.
+    ///     let current_weather = use_resource(move || async move {
+    ///         sleep(delay).await;
+    ///         "Sunny"
+    ///     })
+    ///     .use_dependencies((&delay,));
+    ///
+    ///     rsx! {
+    ///         // the value of the resource can be polled to
+    ///         // conditionally render elements based off if it's future
+    ///         // finished (Some(Ok(_)), errored Some(Err(_)),
+    ///         // or is still running (None)
+    ///         match &*current_weather.read_unchecked() {
+    ///             Some(weather) => rsx! { "{weather}" },
+    ///             None =>  rsx! { p { "Loading..." } }
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    pub fn use_dependencies(mut self, dependency: impl Dependency) -> Self {
+        let mut dependencies_signal = use_signal(|| dependency.out());
+        let changed = { dependency.changed(&*dependencies_signal.read()) };
+        if changed {
+            dependencies_signal.set(dependency.out());
+            self.mark_dirty();
+        }
+        self
+    }
+
+    /// Marks the effect as dirty, causing it to rerun on the next render.
+    pub fn mark_dirty(&mut self) {
+        self.rc.mark_dirty();
+    }
 }

+ 4 - 97
packages/hooks/src/use_memo.rs

@@ -1,14 +1,10 @@
-use crate::dependency::Dependency;
-use crate::{use_callback, use_signal};
+use crate::use_callback;
 use dioxus_core::prelude::*;
-use dioxus_signals::Memo;
-use dioxus_signals::{ReactiveContext, ReadOnlySignal, Readable, Signal, SignalData};
-use dioxus_signals::{Storage, Writable};
-use futures_util::StreamExt;
+use dioxus_signals::{Memo, Signal};
 
-/// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
+/// Creates a new  Memo. The memo will be run immediately and whenever any signal it reads changes.
 ///
-/// Selectors can be used to efficiently compute derived data from signals.
+/// Memos can be used to efficiently compute derived data from signals.
 ///
 /// ```rust
 /// use dioxus::prelude::*;
@@ -29,92 +25,3 @@ pub fn use_memo<R: PartialEq>(f: impl FnMut() -> R + 'static) -> Memo<R> {
     #[allow(clippy::redundant_closure)]
     use_hook(|| Signal::memo(move || callback()))
 }
-
-/// Creates a new unsync Selector with some local dependencies. The selector will be run immediately and whenever any signal it reads or any dependencies it tracks changes
-///
-/// Selectors can be used to efficiently compute derived data from signals.
-///
-/// ```rust
-/// use dioxus::prelude::*;
-///
-/// fn App() -> Element {
-///     let mut local_state = use_signal(|| 0);
-///     let double = use_memo_with_dependencies((&local_state(),), move |(local_state,)| local_state * 2);
-///     local_state.set(1);
-///
-///     rsx! { "{double}" }
-/// }
-/// ```
-#[track_caller]
-pub fn use_memo_with_dependencies<R: PartialEq, D: Dependency>(
-    dependencies: D,
-    f: impl FnMut(D::Out) -> R + 'static,
-) -> ReadOnlySignal<R>
-where
-    D::Out: 'static,
-{
-    use_maybe_sync_memo_with_dependencies(dependencies, f)
-}
-
-/// Creates a new Selector that may be sync with some local dependencies. The selector will be run immediately and whenever any signal it reads or any dependencies it tracks changes
-///
-/// Selectors can be used to efficiently compute derived data from signals.
-///
-/// ```rust
-/// use dioxus::prelude::*;
-/// use dioxus_signals::*;
-///
-/// fn App() -> Element {
-///     let mut local_state = use_signal(|| 0i32);
-///     let double: ReadOnlySignal<i32, SyncStorage> = use_maybe_sync_memo_with_dependencies((&local_state(),), move |(local_state,)| local_state * 2);
-///     local_state.set(1);
-///
-///     rsx! { "{double}" }
-/// }
-/// ```
-#[track_caller]
-pub fn use_maybe_sync_memo_with_dependencies<
-    R: PartialEq,
-    D: Dependency,
-    S: Storage<SignalData<R>>,
->(
-    dependencies: D,
-    mut f: impl FnMut(D::Out) -> R + 'static,
-) -> ReadOnlySignal<R, S>
-where
-    D::Out: 'static,
-{
-    let mut dependencies_signal = use_signal(|| dependencies.out());
-
-    let selector = use_hook(|| {
-        // Get the current reactive context
-        let (rc, mut changed) = ReactiveContext::new();
-
-        // Create a new signal in that context, wiring up its dependencies and subscribers
-        let mut state: Signal<R, S> =
-            rc.run_in(|| Signal::new_maybe_sync(f(dependencies_signal.read().clone())));
-
-        spawn(async move {
-            loop {
-                // Wait for context to change
-                let _ = changed.next().await;
-
-                let new = rc.run_in(|| f(dependencies_signal.read().clone()));
-                if new != *state.peek() {
-                    *state.write() = new;
-                }
-            }
-        });
-
-        // And just return the readonly variant of that signal
-        ReadOnlySignal::new_maybe_sync(state)
-    });
-
-    // This will cause a re-run of the selector if the dependencies change
-    let changed = { dependencies.changed(&*dependencies_signal.read()) };
-    if changed {
-        dependencies_signal.set(dependencies.out());
-    }
-
-    selector
-}

+ 3 - 2
packages/hooks/src/use_resource.rs

@@ -1,6 +1,6 @@
 #![allow(missing_docs)]
 
-use crate::{dependency, use_callback, use_signal, UseCallback};
+use crate::{use_callback, use_signal, UseCallback};
 use dioxus_core::prelude::*;
 use dioxus_core::{
     prelude::{spawn, use_hook},
@@ -156,11 +156,12 @@ impl<T> Resource<T> {
     ///     }
     /// }
     /// ```
-    pub fn use_dependencies(self, dependency: impl dependency::Dependency) -> Self {
+    pub fn use_dependencies(mut self, dependency: impl Dependency) -> Self {
         let mut dependencies_signal = use_signal(|| dependency.out());
         let changed = { dependency.changed(&*dependencies_signal.read()) };
         if changed {
             dependencies_signal.set(dependency.out());
+            self.restart();
         }
         self
     }

+ 0 - 0
packages/hooks/src/dependency.rs → packages/signals/src/dependency.rs


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

@@ -19,6 +19,9 @@ pub use map::*;
 // mod comparer;
 // pub use comparer::*;
 
+mod dependency;
+pub use dependency::*;
+
 mod memo;
 pub use memo::*;
 

+ 35 - 1
packages/signals/src/memo.rs

@@ -1,6 +1,6 @@
 use crate::write::Writable;
 use crate::{read::Readable, ReactiveContext, ReadableRef, Signal};
-use crate::{CopyValue, ReadOnlySignal};
+use crate::{CopyValue, Dependency, ReadOnlySignal};
 use std::rc::Rc;
 use std::{
     cell::RefCell,
@@ -153,6 +153,40 @@ impl<T: 'static> Memo<T> {
     pub fn id(&self) -> generational_box::GenerationalBoxId {
         self.inner.id()
     }
+
+    /// Adds an explicit dependency to the memo. If the dependency changes, the memo will rerun.
+    ///
+    /// Signals will automatically be added as dependencies, so you don't need to call this method for them.
+    ///
+    /// NOTE: You must follow the rules of hooks when calling this method.
+    ///
+    /// ```rust
+    /// # use dioxus::prelude::*;
+    /// # async fn sleep(delay: u32) {}
+    ///
+    /// #[component]
+    /// fn Comp(count: u32) -> Element {
+    ///     // Because the resource subscribes to `delay` by adding it as a dependency, the memo will rerun every time `count` changes.
+    ///     let new_count = use_memo(move || async move {
+    ///         count + 1
+    ///     })
+    ///     .use_dependencies((&count,));
+    ///
+    ///     todo!()
+    /// }
+    /// ```
+    pub fn use_dependencies(self, dependency: impl Dependency) -> Self
+    where
+        T: PartialEq,
+    {
+        let mut dependencies_signal = use_hook(|| CopyValue::new(dependency.out()));
+        let changed = { dependency.changed(&*dependencies_signal.read()) };
+        if changed {
+            dependencies_signal.set(dependency.out());
+            self.recompute();
+        }
+        self
+    }
 }
 
 impl<T> Readable for Memo<T>