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
25pub struct UseEffectImpl<D> {
26    deps: Option<D>,
27}
28
29impl<D> Default for UseEffectImpl<D> {
30    fn default() -> Self {
31        Self { deps: None }
32    }
33}
34
35pub struct UseAsyncEffectImpl<D> {
36    f: Option<LocalBoxFuture<'static, ()>>,
37    deps: Option<D>,
38}
39
40impl<D> Default for UseAsyncEffectImpl<D> {
41    fn default() -> Self {
42        Self {
43            f: None,
44            deps: None,
45        }
46    }
47}
48
49impl<D: Unpin> Hook for UseAsyncEffectImpl<D> {
50    fn poll_change(&mut self, cx: &mut std::task::Context) -> std::task::Poll<()> {
51        if let Some(future) = self.f.as_mut()
52            && future.as_mut().poll(cx).is_ready()
53        {
54            self.f = None;
55            return Poll::Ready(());
56        }
57        Poll::Pending
58    }
59}
60
61impl<D: Unpin> Hook for UseEffectImpl<D> {}
62
63impl UseEffect for Hooks<'_, '_> {
64    fn use_effect<F, D>(&mut self, f: F, deps: D)
65    where
66        F: FnOnce(),
67        D: PartialEq + Unpin + 'static,
68    {
69        let hook = self.use_hook(UseEffectImpl::<D>::default);
70        if hook.deps.as_ref() != Some(&deps) {
71            f();
72            hook.deps = Some(deps);
73        }
74    }
75
76    fn use_async_effect<F, D>(&mut self, f: F, deps: D)
77    where
78        F: Future<Output = ()> + 'static,
79        D: PartialEq + Unpin + 'static,
80    {
81        let hook = self.use_hook(UseAsyncEffectImpl::<D>::default);
82
83        if hook.deps.as_ref() != Some(&deps) {
84            hook.f = Some(f.boxed_local());
85            hook.deps = Some(deps);
86        }
87    }
88}