* clear away prelude on dioxus * clippy * wip - drop prelude from dioxus-html * docs clippy * wip * more fixe * delete all preludes * doc... * better export * merge some imports * fewer global imports * global incremental renderer * fix cargo doc
65 lines
1.9 KiB
Rust
65 lines
1.9 KiB
Rust
use std::{cell::Cell, rc::Rc};
|
|
|
|
use dioxus_core::*;
|
|
use futures_util::StreamExt;
|
|
|
|
use crate::use_callback;
|
|
|
|
#[doc = include_str!("../docs/side_effects.md")]
|
|
#[doc = include_str!("../docs/rules_of_hooks.md")]
|
|
#[track_caller]
|
|
pub fn use_effect(mut callback: impl FnMut() + 'static) -> Effect {
|
|
let callback = use_callback(move |_| callback());
|
|
|
|
let location = std::panic::Location::caller();
|
|
|
|
use_hook(|| {
|
|
// Inside the effect, we track any reads so that we can rerun the effect if a value the effect reads changes
|
|
let (rc, mut changed) = ReactiveContext::new_with_origin(location);
|
|
|
|
// Deduplicate queued effects
|
|
let effect_queued = Rc::new(Cell::new(false));
|
|
|
|
// Spawn a task that will run the effect when:
|
|
// 1) The component is first run
|
|
// 2) The effect is rerun due to an async read at any time
|
|
// 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
|
|
let queue_effect_for_next_render = move || {
|
|
if effect_queued.get() {
|
|
return;
|
|
}
|
|
effect_queued.set(true);
|
|
let effect_queued = effect_queued.clone();
|
|
queue_effect(move || {
|
|
rc.reset_and_run_in(|| callback(()));
|
|
effect_queued.set(false);
|
|
});
|
|
};
|
|
|
|
queue_effect_for_next_render();
|
|
spawn(async move {
|
|
loop {
|
|
// Wait for context to change
|
|
let _ = changed.next().await;
|
|
|
|
// Run the effect
|
|
queue_effect_for_next_render();
|
|
}
|
|
});
|
|
Effect { rc }
|
|
})
|
|
}
|
|
|
|
/// A handle to an effect.
|
|
#[derive(Clone, Copy)]
|
|
pub struct Effect {
|
|
rc: ReactiveContext,
|
|
}
|
|
|
|
impl Effect {
|
|
/// Marks the effect as dirty, causing it to rerun on the next render.
|
|
pub fn mark_dirty(&mut self) {
|
|
self.rc.mark_dirty();
|
|
}
|
|
}
|