effect.rs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. use core::{self, fmt::Debug};
  2. use std::fmt::{self, Formatter};
  3. //
  4. use dioxus_core::prelude::*;
  5. use crate::use_signal;
  6. use crate::{dependency::Dependency, CopyValue};
  7. #[derive(Copy, Clone, PartialEq)]
  8. pub(crate) struct EffectStack {
  9. pub(crate) effects: CopyValue<Vec<Effect>>,
  10. }
  11. impl Default for EffectStack {
  12. fn default() -> Self {
  13. Self {
  14. effects: CopyValue::new_in_scope(Vec::new(), ScopeId::ROOT),
  15. }
  16. }
  17. }
  18. impl EffectStack {
  19. pub(crate) fn current(&self) -> Option<Effect> {
  20. self.effects.read().last().copied()
  21. }
  22. }
  23. pub(crate) fn get_effect_stack() -> EffectStack {
  24. match consume_context() {
  25. Some(rt) => rt,
  26. None => {
  27. let store = EffectStack::default();
  28. provide_root_context(store);
  29. store
  30. }
  31. }
  32. }
  33. /// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
  34. /// The signal will be owned by the current component and will be dropped when the component is dropped.
  35. pub fn use_effect(callback: impl FnMut() + 'static) {
  36. use_hook(|| Effect::new(callback));
  37. }
  38. /// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
  39. /// The signal will be owned by the current component and will be dropped when the component is dropped.
  40. pub fn use_effect_with_dependencies<D: Dependency>(
  41. dependencies: D,
  42. mut callback: impl FnMut(D::Out) + 'static,
  43. ) where
  44. D::Out: 'static,
  45. {
  46. let dependencies_signal = use_signal(|| dependencies.out());
  47. use_hook(|| {
  48. Effect::new(move || {
  49. let deref = &*dependencies_signal.read();
  50. callback(deref.clone());
  51. });
  52. });
  53. let changed = { dependencies.changed(&*dependencies_signal.read()) };
  54. if changed {
  55. dependencies_signal.set(dependencies.out());
  56. }
  57. }
  58. /// Effects allow you to run code when a signal changes. Effects are run immediately and whenever any signal it reads changes.
  59. #[derive(Copy, Clone, PartialEq)]
  60. pub struct Effect {
  61. pub(crate) source: ScopeId,
  62. pub(crate) callback: CopyValue<Box<dyn FnMut()>>,
  63. pub(crate) effect_stack: EffectStack,
  64. }
  65. impl Debug for Effect {
  66. fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
  67. f.write_fmt(format_args!("{:?}", self.callback.value))
  68. }
  69. }
  70. impl Effect {
  71. pub(crate) fn current() -> Option<Self> {
  72. get_effect_stack().effects.read().last().copied()
  73. }
  74. /// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
  75. ///
  76. /// The signal will be owned by the current component and will be dropped when the component is dropped.
  77. pub fn new(callback: impl FnMut() + 'static) -> Self {
  78. let myself = Self {
  79. source: current_scope_id().expect("in a virtual dom"),
  80. callback: CopyValue::new(Box::new(callback)),
  81. effect_stack: get_effect_stack(),
  82. };
  83. myself.try_run();
  84. myself
  85. }
  86. /// Run the effect callback immediately. Returns `true` if the effect was run. Returns `false` is the effect is dead.
  87. pub fn try_run(&self) {
  88. if let Ok(mut callback) = self.callback.try_write() {
  89. {
  90. self.effect_stack.effects.write().push(*self);
  91. }
  92. callback();
  93. {
  94. self.effect_stack.effects.write().pop();
  95. }
  96. }
  97. }
  98. }