dioxus_hooks/
use_effect.rs

1use std::{cell::Cell, rc::Rc};
2
3use dioxus_core::prelude::*;
4use futures_util::StreamExt;
5
6use crate::use_callback;
7
8#[doc = include_str!("../docs/side_effects.md")]
9#[doc = include_str!("../docs/rules_of_hooks.md")]
10#[track_caller]
11pub fn use_effect(mut callback: impl FnMut() + 'static) -> Effect {
12    let callback = use_callback(move |_| callback());
13
14    let location = std::panic::Location::caller();
15
16    use_hook(|| {
17        // Inside the effect, we track any reads so that we can rerun the effect if a value the effect reads changes
18        let (rc, mut changed) = ReactiveContext::new_with_origin(location);
19
20        // Deduplicate queued effects
21        let effect_queued = Rc::new(Cell::new(false));
22
23        // Spawn a task that will run the effect when:
24        // 1) The component is first run
25        // 2) The effect is rerun due to an async read at any time
26        // 3) The effect is rerun in the same tick that the component is rerun: we need to wait for the component to rerun before we can run the effect again
27        let queue_effect_for_next_render = move || {
28            if effect_queued.get() {
29                return;
30            }
31            effect_queued.set(true);
32            let effect_queued = effect_queued.clone();
33            queue_effect(move || {
34                rc.reset_and_run_in(|| callback(()));
35                effect_queued.set(false);
36            });
37        };
38
39        queue_effect_for_next_render();
40        spawn(async move {
41            loop {
42                // Wait for context to change
43                let _ = changed.next().await;
44
45                // Run the effect
46                queue_effect_for_next_render();
47            }
48        });
49        Effect { rc }
50    })
51}
52
53/// A handle to an effect.
54#[derive(Clone, Copy)]
55pub struct Effect {
56    rc: ReactiveContext,
57}
58
59impl Effect {
60    /// Marks the effect as dirty, causing it to rerun on the next render.
61    pub fn mark_dirty(&mut self) {
62        self.rc.mark_dirty();
63    }
64}