hooks/effect/
hook_once.rs1use super::{inner::Cleanup, EffectForNoneDependency};
2
3pub struct EffectOnce<E: EffectForNoneDependency> {
4 inner: Result<Cleanup<E::Cleanup>, E>,
8}
9
10impl<E: EffectForNoneDependency> Unpin for EffectOnce<E> {}
11
12impl<E: EffectForNoneDependency> Default for EffectOnce<E> {
13 #[inline]
14 fn default() -> Self {
15 Self {
16 inner: Ok(Cleanup(None)),
17 }
18 }
19}
20
21impl<E: EffectForNoneDependency> std::fmt::Debug for EffectOnce<E> {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 f.debug_tuple("EffectOnce")
24 .field(&match &self.inner {
25 Err(_) => "effect",
26 Ok(Cleanup(Some(_))) => "effected",
27 Ok(Cleanup(None)) => "uninitialized",
28 })
29 .finish()
30 }
31}
32
33impl<E: EffectForNoneDependency> EffectOnce<E> {
34 pub fn register_effect_with(&mut self, get_effect: impl FnOnce() -> E) {
35 let inner = &mut self.inner;
36 if let Ok(Cleanup(None)) = &inner {
37 *inner = Err(get_effect())
38 }
39 }
40
41 #[inline]
42 fn impl_poll(&mut self) -> std::task::Poll<bool> {
43 let inner = &mut self.inner;
44 if inner.is_err() {
45 let effect = match std::mem::replace(inner, Ok(Cleanup(None))) {
46 Err(effect) => effect,
47 _ => unreachable!(),
48 };
49 let cleanup = effect.effect_for_none_dep();
50 *inner = Ok(Cleanup(Some(cleanup)));
51 }
52 std::task::Poll::Ready(false)
53 }
54}
55
56hooks_core::impl_hook![
57 impl<E: EffectForNoneDependency> EffectOnce<E> {
58 #[inline]
59 fn unmount(self) {
60 drop(std::mem::take(self.get_mut()))
61 }
62 #[inline]
63 fn poll_next_update(self) {
64 self.get_mut().impl_poll()
65 }
66 #[inline]
67 fn use_hook(self) -> () {}
68 }
69];
70
71pub struct UseEffectOnce<E>(pub E);
72pub use UseEffectOnce as use_effect_once;
73
74hooks_core::impl_hook![
75 impl<E: EffectForNoneDependency> UseEffectOnce<E> {
76 #[inline]
77 fn into_hook(self) -> EffectOnce<E> {
78 EffectOnce { inner: Err(self.0) }
79 }
80 #[inline]
81 fn update_hook(self, hook: _) {
82 hook.get_mut().register_effect_with(move || self.0)
83 }
84 #[inline]
85 fn h(self, hook: EffectOnce<E>) {
86 hooks_core::UpdateHook::update_hook(self, hook)
87 }
88 }
89];
90
91pub struct UseEffectOnceWith<F>(pub F);
92
93#[inline(always)]
94pub fn use_effect_once_with<E: EffectForNoneDependency>(
95 get_effect: impl FnOnce() -> E,
96) -> UseEffectOnceWith<impl FnOnce() -> E> {
97 UseEffectOnceWith(get_effect)
98}
99
100hooks_core::impl_hook![
101 impl<E: EffectForNoneDependency, F: FnOnce() -> E> UseEffectOnceWith<F> {
102 #[inline]
103 fn into_hook(self) -> EffectOnce<E> {
104 EffectOnce {
105 inner: Err(self.0()),
106 }
107 }
108 #[inline]
109 fn update_hook(self, hook: _) {
110 hook.get_mut().register_effect_with(self.0)
111 }
112 #[inline]
113 fn h(self, hook: EffectOnce<E>) {
114 hooks_core::UpdateHook::update_hook(self, hook)
115 }
116 }
117];
118
119#[cfg(test)]
120mod tests {
121 use std::cell::RefCell;
122
123 use hooks_core::{HookExt, IntoHook};
124
125 #[test]
126 fn effect_once() {
127 let effected = RefCell::new(Vec::with_capacity(2));
128
129 {
130 let effect = || {
131 let mut e = effected.borrow_mut();
132 assert!(e.is_empty());
133 e.push("effected");
134
135 || effected.borrow_mut().push("cleaned")
136 };
137
138 let hook = super::use_effect_once(effect).into_hook();
139
140 futures_lite::pin!(hook);
141
142 futures_lite::future::block_on(async {
143 assert!(hook.next_value().await.is_none());
144 assert_eq!(*effected.borrow(), ["effected"]);
145 });
146 }
147
148 assert_eq!(effected.into_inner(), ["effected", "cleaned"]);
149 }
150}