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 returns Poll::Pending), it is producing the next item.

  • If a Hook is pending, (poll_next_update returns Poll::Pending), it is waiting for its inner state to update. When a Hook is pending, the executor can still use it by calling use_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 in use_hook. For example, use_state_clone clones the data in use_hook. It is advised to call use_hook only after poll_next_update returns Poll::Ready(true).

For terminating:

  • If a LendingAsyncIterator is terminated (poll_next returns Poll::Ready(None)), the executor MUST NOT call poll_next again.

  • There is no termination for a Hook until dropped. When poll_next_update returns Poll::Ready(false), this means the hook is no longer dynamic (its inner state will no longer update). Thus, there is no need to call use_hook again because the returned value is expected to remain the same. But the executor can still call use_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.

Required Methods§

Implementations on Foreign Types§

Implementors§