hooks/
lazy_pinned_hook.rs1use std::{marker::PhantomData, pin::Pin};
2
3use hooks_core::{Hook, HookPollNextUpdate, HookUnmount, UpdateHook};
4
5pin_project_lite::pin_project![
6 #[derive(Debug)]
7 pub struct LazyPinnedHook<H>
8 where
9 H: HookPollNextUpdate,
10 H: HookUnmount,
11 {
12 #[pin]
13 hook: Option<H>,
14 }
15];
16
17impl<H: HookPollNextUpdate + HookUnmount> Default for LazyPinnedHook<H> {
18 fn default() -> Self {
19 Self { hook: None }
20 }
21}
22
23impl<H: HookPollNextUpdate + HookUnmount> LazyPinnedHook<H> {
24 #[inline]
25 pub fn pin_project(self: Pin<&mut Self>) -> Pin<&mut Option<H>> {
26 self.project().hook
27 }
28
29 #[inline]
30 pub fn pin_project_hook(self: Pin<&mut Self>) -> Option<Pin<&mut H>> {
31 self.pin_project().as_pin_mut()
32 }
33}
34impl<H: Hook> LazyPinnedHook<H> {
35 pub fn h(mut self: Pin<&mut Self>, into_hook: impl UpdateHook<Hook = H>) -> crate::Value<H> {
36 if let Some(hook) = self.as_mut().pin_project_hook() {
37 into_hook.update_hook(hook)
38 } else {
39 self.as_mut().pin_project().set(Some(into_hook.into_hook()))
40 }
41
42 H::use_hook(self.pin_project_hook().unwrap())
43 }
44}
45
46hooks_core::impl_hook![
47 impl<H: HookPollNextUpdate + HookUnmount> LazyPinnedHook<H> {
48 fn unmount(self) {
49 if let Some(hook) = self.pin_project_hook() {
50 H::unmount(hook)
51 }
52 }
53
54 fn poll_next_update(self, cx: _) {
55 let hook = self.pin_project_hook();
56 if let Some(hook) = hook {
57 <H as HookPollNextUpdate>::poll_next_update(hook, cx)
58 } else {
59 std::task::Poll::Ready(false)
60 }
61 }
62
63 #[inline(always)]
64 fn use_hook(self) -> Pin<&'hook mut Self> {
65 self
66 }
67 }
68];
69
70pub struct UseLazyPinnedHook<H: Hook>(PhantomData<H>);
71
72#[cfg_attr(
80 all(
81 feature = "futures-core",
82 feature = "proc-macro",
83 feature = "use_shared_set",
84 feature = "use_effect",
85 ),
86 doc = r###"
87```
88# use hooks::prelude::*;
89#[hook]
90fn use_demo() -> i32 {
91 let (state, updater) = use_shared_set(0);
92 if *state < 2 {
93 use_effect(|v: &i32| updater.set(*v + 1), *state);
94 }
95 *state
96}
97
98# use futures_lite::StreamExt;
99# futures_lite::future::block_on(async {
100let values: Vec<_> = use_demo().into_hook_values().collect().await;
101assert_eq!(values, [0])
102# });
103```
104"###
105)]
106#[cfg_attr(
110 all(
111 feature = "futures-core",
112 feature = "proc-macro",
113 feature = "use_shared_set",
114 feature = "use_effect",
115 ),
116 doc = r###"
117```
118# use hooks::prelude::*;
119#[hook]
120fn use_demo() -> i32 {
121 let (state, updater) = use_shared_set(0);
122 let hook_effect = use_lazy_pinned_hook();
123 if *state < 2 {
124 let updater = updater.clone();
125 hook_effect.h(use_effect(move |v: &i32| updater.set(*v + 1), *state));
126 }
127 *state
128}
129
130# use futures_lite::StreamExt;
131# futures_lite::future::block_on(async {
132let values: Vec<_> = use_demo().into_hook_values().collect().await;
133assert_eq!(values, [0, 1, 2])
134# });
135```
136"###
137)]
138#[inline(always)]
139pub fn use_lazy_pinned_hook<H: Hook>() -> UseLazyPinnedHook<H> {
140 UseLazyPinnedHook(PhantomData)
141}
142
143hooks_core::impl_hook![
144 impl<H: Hook> UseLazyPinnedHook<H> {
145 #[inline]
146 fn into_hook(self) -> LazyPinnedHook<H> {
147 LazyPinnedHook::default()
148 }
149
150 #[inline]
151 fn update_hook(self, _hook: _) {}
152
153 #[inline]
154 fn h(self, hook: LazyPinnedHook<H>) {
155 hook
156 }
157 }
158];