Trait hooks_core::Hook
source · pub trait Hook<Args>: HookPollNextUpdate + for<'hook> HookLifetime<'hook, Args> {
fn use_hook<'hook>(
self: Pin<&'hook mut Self>,
args: Args
) -> <Self as HookLifetime<'hook, Args>>::Value
where
Self: 'hook;
}
Expand description
How to impl Hook
with [hook
] macro
Usually, we just need a function which returns a hook,
without needing a type which implements Hook
.
With hook
macro, we can do this easily.
/// Print debug on `value` change.
#[hook]
fn use_debug<'a, T: std::fmt::Debug + Eq + 'a>(value: &'a T) {
use_effect(|v: &_| {
println!("{v:?}");
}, value);
}
implement Hook
manually.
To implement Hook
for a type, implement
HookBounds
, [HookLifetime<’hook>] and HookPollNextUpdate
first.
struct MyHook<T>(Option<T>);
impl<T> HookBounds for MyHook<T> {
type Bounds = Self;
}
impl<'hook, T> HookLifetime<'hook, (T,), &'hook Self> for MyHook<T> {
// ^^^^^^^^^^^
// This must be exactly
// `&'hook <Self as HookBounds>::Bounds`
type Value = &'hook T;
// ^^^^^^^^ We can write `&'hook T` without
// implicitly specifying `T: 'hook`
// because `&'hook Self` implies this.
}
impl<T> HookPollNextUpdate for MyHook<T> {
fn poll_next_update(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<bool> {
todo!()
}
}
Comparison with LendingAsyncIterator
A Hook
is like a LendingAsyncIterator
.
They both produce items asynchronously,
but they have different meanings on pending and terminating:
For pending:
-
If a
LendingAsyncIterator
is pending (poll_next
returnsPoll::Pending
), it is producing the next item. -
If a
Hook
is pending, (poll_next_update
returnsPoll::Pending
), it is waiting for its inner state to update. When aHook
is pending, the executor can still use it by callinguse_hook
and the returned value would remain the same as the last returned value. Using a hook is like inspecting it. Some hooks may do heavy work inuse_hook
. For example,use_state_clone
clones the data inuse_hook
. It is advised to calluse_hook
only afterpoll_next_update
returnsPoll::Ready(true)
.
For terminating:
-
If a
LendingAsyncIterator
is terminated (poll_next
returnsPoll::Ready(None)
), the executor MUST NOT callpoll_next
again. -
There is no termination for a
Hook
until dropped. Whenpoll_next_update
returnsPoll::Ready(false)
, this means the hook is no longer dynamic (its inner state will no longer update). Thus, there is no need to calluse_hook
again because the returned value is expected to remain the same. But the executor can still calluse_hook
to re-get the value and this might make the hook dynamic again.This behavior makes it possible to combine multiple hooks. When some hooks are no longer dynamic but other hooks depend on their returned values, the executor can still get the values from the no-longer-dynamic hooks, and pass the values to the dynamic hooks.