Skip to main content

ratatui_kit/hooks/
use_effect.rs

1use futures::{FutureExt, future::LocalBoxFuture};
2use std::task::Poll;
3
4use crate::{Hook, Hooks};
5
6mod private {
7    pub trait Sealed {}
8    impl Sealed for crate::Hooks<'_, '_> {}
9}
10
11pub trait UseEffect: private::Sealed {
12    // 注册同步副作用,依赖变化时自动执行,适合监听状态变化、同步校验等。
13    fn use_effect<F, D>(&mut self, f: F, deps: D)
14    where
15        F: FnOnce(),
16        D: PartialEq + Unpin + 'static;
17
18    // 注册异步副作用,依赖变化时自动执行,适合异步校验、异步请求等。
19    fn use_async_effect<F, D>(&mut self, f: F, deps: D)
20    where
21        F: Future<Output = ()> + 'static,
22        D: PartialEq + Unpin + 'static;
23}
24
25#[doc(hidden)]
26pub struct UseEffectImpl<D> {
27    deps: Option<D>,
28}
29
30impl<D> Default for UseEffectImpl<D> {
31    fn default() -> Self {
32        Self { deps: None }
33    }
34}
35
36#[doc(hidden)]
37pub struct UseAsyncEffectImpl<D> {
38    f: Option<LocalBoxFuture<'static, ()>>,
39    deps: Option<D>,
40}
41
42impl<D> Default for UseAsyncEffectImpl<D> {
43    fn default() -> Self {
44        Self {
45            f: None,
46            deps: None,
47        }
48    }
49}
50
51impl<D: Unpin> Hook for UseAsyncEffectImpl<D> {
52    fn poll_change(&mut self, cx: &mut std::task::Context) -> std::task::Poll<()> {
53        if let Some(future) = self.f.as_mut()
54            && future.as_mut().poll(cx).is_ready()
55        {
56            self.f = None;
57            return Poll::Ready(());
58        }
59        Poll::Pending
60    }
61}
62
63impl<D: Unpin> Hook for UseEffectImpl<D> {}
64
65impl UseEffect for Hooks<'_, '_> {
66    fn use_effect<F, D>(&mut self, f: F, deps: D)
67    where
68        F: FnOnce(),
69        D: PartialEq + Unpin + 'static,
70    {
71        let hook = self.use_hook(UseEffectImpl::<D>::default);
72        if hook.deps.as_ref() != Some(&deps) {
73            f();
74            hook.deps = Some(deps);
75        }
76    }
77
78    fn use_async_effect<F, D>(&mut self, f: F, deps: D)
79    where
80        F: Future<Output = ()> + 'static,
81        D: PartialEq + Unpin + 'static,
82    {
83        let hook = self.use_hook(UseAsyncEffectImpl::<D>::default);
84
85        if hook.deps.as_ref() != Some(&deps) {
86            hook.f = Some(f.boxed_local());
87            hook.deps = Some(deps);
88        }
89    }
90}