use_reactive.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. use dioxus_signals::{Readable, Writable};
  2. use crate::use_signal;
  3. /// A dependency is a trait that can be used to determine if a effect or selector should be re-run.
  4. #[rustversion::attr(
  5. since(1.78.0),
  6. diagnostic::on_unimplemented(
  7. message = "`Dependency` is not implemented for `{Self}`",
  8. label = "Dependency",
  9. note = "Dependency is automatically implemented for all tuples with less than 8 references to element that implement `PartialEq` and `Clone`. For example, `(&A, &B, &C)` implements `Dependency` automatically as long as `A`, `B`, and `C` implement `PartialEq` and `Clone`.",
  10. )
  11. )]
  12. pub trait Dependency: Sized + Clone {
  13. /// The output of the dependency
  14. type Out: Clone + PartialEq + 'static;
  15. /// Returns the output of the dependency.
  16. fn out(&self) -> Self::Out;
  17. /// Returns true if the dependency has changed.
  18. fn changed(&self, other: &Self::Out) -> bool {
  19. self.out() != *other
  20. }
  21. }
  22. impl Dependency for () {
  23. type Out = ();
  24. fn out(&self) -> Self::Out {}
  25. }
  26. /// A dependency is a trait that can be used to determine if a effect or selector should be re-run.
  27. #[rustversion::attr(
  28. since(1.78.0),
  29. diagnostic::on_unimplemented(
  30. message = "`DependencyElement` is not implemented for `{Self}`",
  31. label = "dependency element",
  32. note = "DependencyElement is automatically implemented for types that implement `PartialEq` and `Clone`",
  33. )
  34. )]
  35. pub trait DependencyElement: 'static + PartialEq + Clone {}
  36. impl<T> DependencyElement for T where T: 'static + PartialEq + Clone {}
  37. impl<A: DependencyElement> Dependency for &A {
  38. type Out = A;
  39. fn out(&self) -> Self::Out {
  40. (*self).clone()
  41. }
  42. }
  43. macro_rules! impl_dep {
  44. (
  45. $($el:ident=$name:ident $other:ident,)*
  46. ) => {
  47. impl< $($el),* > Dependency for ($(&$el,)*)
  48. where
  49. $(
  50. $el: DependencyElement
  51. ),*
  52. {
  53. type Out = ($($el,)*);
  54. fn out(&self) -> Self::Out {
  55. let ($($name,)*) = self;
  56. ($((*$name).clone(),)*)
  57. }
  58. fn changed(&self, other: &Self::Out) -> bool {
  59. let ($($name,)*) = self;
  60. let ($($other,)*) = other;
  61. $(
  62. if *$name != $other {
  63. return true;
  64. }
  65. )*
  66. false
  67. }
  68. }
  69. };
  70. }
  71. impl_dep!(A = a1 a2,);
  72. impl_dep!(A = a1 a2, B = b1 b2,);
  73. impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2,);
  74. impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2,);
  75. impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2,);
  76. impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2, F = f1 f2,);
  77. impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2, F = f1 f2, G = g1 g2,);
  78. impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2, F = f1 f2, G = g1 g2, H = h1 h2,);
  79. /// Takes some non-reactive data, and a closure and returns a closure that will subscribe to that non-reactive data as if it were reactive.
  80. ///
  81. /// # Example
  82. ///
  83. /// ```rust, no_run
  84. /// use dioxus::prelude::*;
  85. ///
  86. /// let data = 5;
  87. ///
  88. /// use_effect(use_reactive((&data,), |(data,)| {
  89. /// println!("Data changed: {}", data);
  90. /// }));
  91. /// ```
  92. #[doc = include_str!("../docs/rules_of_hooks.md")]
  93. pub fn use_reactive<O, D: Dependency>(
  94. non_reactive_data: D,
  95. mut closure: impl FnMut(D::Out) -> O + 'static,
  96. ) -> impl FnMut() -> O + 'static {
  97. let mut first_run = false;
  98. let mut last_state = use_signal(|| {
  99. first_run = true;
  100. non_reactive_data.out()
  101. });
  102. if !first_run && non_reactive_data.changed(&*last_state.peek()) {
  103. last_state.set(non_reactive_data.out())
  104. }
  105. move || closure(last_state())
  106. }
  107. /// A helper macro for `use_reactive` that merges uses the closure syntax to elaborate the dependency array
  108. ///
  109. /// Takes some non-reactive data, and a closure and returns a closure that will subscribe to that non-reactive data as if it were reactive.
  110. ///
  111. /// # Example
  112. ///
  113. /// ```rust, no_run
  114. /// use dioxus::prelude::*;
  115. ///
  116. /// let data = 5;
  117. ///
  118. /// use_effect(use_reactive!(|data| {
  119. /// println!("Data changed: {}", data);
  120. /// }));
  121. /// ```
  122. #[doc = include_str!("../docs/rules_of_hooks.md")]
  123. #[macro_export]
  124. macro_rules! use_reactive {
  125. (|| $($rest:tt)*) => { use_reactive( (), move |_| $($rest)* ) };
  126. (| $($args:tt),* | $($rest:tt)*) => {
  127. use_reactive(
  128. ($(&$args),*),
  129. move |($($args),*)| $($rest)*
  130. )
  131. };
  132. }