add an optional cleanup closure to the use effect hook
This commit is contained in:
1 parent
098689083d
commit
83f7ef9a44
2 files changed
+76
-9
No files matched your search
@@ -11,6 +11,12 @@ fn main() {
|
|||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let mut count = use_state(cx, || 0);
|
let mut count = use_state(cx, || 0);
|
||||||
|
|
||||||
|
use_effect(cx, (), move |()| async {});
|
||||||
|
|
||||||
|
use_effect(cx, (count.get(),), move |(count,)| async move {
|
||||||
|
move || println!("Count unmounted from {}", count)
|
||||||
|
});
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 { "High-Five counter: {count}" }
|
h1 { "High-Five counter: {count}" }
|
||||||
button { onclick: move |_| count += 1, "Up high!" }
|
button { onclick: move |_| count += 1, "Up high!" }
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
use dioxus_core::{ScopeState, TaskId};
|
use dioxus_core::{ScopeState, TaskId};
|
||||||
use std::{any::Any, cell::Cell, future::Future};
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
future::Future,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::UseFutureDep;
|
use crate::UseFutureDep;
|
||||||
|
|
||||||
@@ -14,7 +19,7 @@ use crate::UseFutureDep;
|
|||||||
/// ## Arguments
|
/// ## Arguments
|
||||||
///
|
///
|
||||||
/// - `dependencies`: a tuple of references to values that are `PartialEq` + `Clone`.
|
/// - `dependencies`: a tuple of references to values that are `PartialEq` + `Clone`.
|
||||||
/// - `future`: a closure that takes the `dependencies` as arguments and returns a `'static` future.
|
/// - `future`: a closure that takes the `dependencies` as arguments and returns a `'static` future. That future may return nothing or a closure that will be executed when the dependencies change to clean up the effect.
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
@@ -33,6 +38,16 @@ use crate::UseFutureDep;
|
|||||||
/// }
|
/// }
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
|
/// // Only fetch the user data when the id changes.
|
||||||
|
/// use_effect(cx, (id,), |(id,)| {
|
||||||
|
/// to_owned![name];
|
||||||
|
/// async move {
|
||||||
|
/// let user = fetch_user(id).await;
|
||||||
|
/// name.set(user.name);
|
||||||
|
/// move || println!("Cleaning up from {}", id);
|
||||||
|
/// }
|
||||||
|
/// });
|
||||||
|
///
|
||||||
/// let name = name.get().clone().unwrap_or("Loading...".to_string());
|
/// let name = name.get().clone().unwrap_or("Loading...".to_string());
|
||||||
///
|
///
|
||||||
/// render!(
|
/// render!(
|
||||||
@@ -45,34 +60,80 @@ use crate::UseFutureDep;
|
|||||||
/// render!(Profile { id: 0 })
|
/// render!(Profile { id: 0 })
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn use_effect<T, F, D>(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F)
|
pub fn use_effect<T, R, D>(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> R)
|
||||||
where
|
where
|
||||||
T: 'static,
|
|
||||||
F: Future<Output = T> + 'static,
|
|
||||||
D: UseFutureDep,
|
D: UseFutureDep,
|
||||||
|
R: UseEffectReturn<T>,
|
||||||
{
|
{
|
||||||
struct UseEffect {
|
struct UseEffect {
|
||||||
needs_regen: bool,
|
needs_regen: bool,
|
||||||
task: Cell<Option<TaskId>>,
|
task: Cell<Option<TaskId>>,
|
||||||
dependencies: Vec<Box<dyn Any>>,
|
dependencies: Vec<Box<dyn Any>>,
|
||||||
|
cleanup: UseEffectCleanup,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UseEffect {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(cleanup) = self.cleanup.borrow_mut().take() {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = cx.use_hook(move || UseEffect {
|
let state = cx.use_hook(move || UseEffect {
|
||||||
needs_regen: true,
|
needs_regen: true,
|
||||||
task: Cell::new(None),
|
task: Cell::new(None),
|
||||||
dependencies: Vec::new(),
|
dependencies: Vec::new(),
|
||||||
|
cleanup: Rc::new(RefCell::new(None)),
|
||||||
});
|
});
|
||||||
|
|
||||||
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen {
|
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen {
|
||||||
|
// Call the cleanup function if it exists
|
||||||
|
if let Some(cleanup) = state.cleanup.borrow_mut().take() {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
// We don't need regen anymore
|
// We don't need regen anymore
|
||||||
state.needs_regen = false;
|
state.needs_regen = false;
|
||||||
|
|
||||||
// Create the new future
|
// Create the new future
|
||||||
let fut = future(dependencies.out());
|
let return_value = future(dependencies.out());
|
||||||
|
|
||||||
state.task.set(Some(cx.push_future(async move {
|
if let Some(task) = return_value.apply(state.cleanup.clone(), cx) {
|
||||||
fut.await;
|
state.task.set(Some(task));
|
||||||
})));
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UseEffectCleanup = Rc<RefCell<Option<Box<dyn FnOnce()>>>>;
|
||||||
|
|
||||||
|
/// Something that can be returned from a `use_effect` hook.
|
||||||
|
pub trait UseEffectReturn<T> {
|
||||||
|
fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Option<TaskId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> UseEffectReturn<()> for T
|
||||||
|
where
|
||||||
|
T: Future<Output = ()> + 'static,
|
||||||
|
{
|
||||||
|
fn apply(self, _: UseEffectCleanup, cx: &ScopeState) -> Option<TaskId> {
|
||||||
|
Some(cx.push_future(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct CleanupFutureMarker;
|
||||||
|
impl<T, F> UseEffectReturn<CleanupFutureMarker> for T
|
||||||
|
where
|
||||||
|
T: Future<Output = F> + 'static,
|
||||||
|
F: FnOnce() + 'static,
|
||||||
|
{
|
||||||
|
fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Option<TaskId> {
|
||||||
|
let task = cx.push_future(async move {
|
||||||
|
let cleanup = self.await;
|
||||||
|
*oncleanup.borrow_mut() = Some(Box::new(cleanup) as Box<dyn FnOnce()>);
|
||||||
|
});
|
||||||
|
Some(task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user