Macro hooks_core::hook_fn
source · macro_rules! hook_fn { ( type Bounds = impl $hook_bound:lifetime $(+ $hook_bounds:lifetime)* ; $($rest:tt)* ) => { ... }; ($($rest:tt)*) => { ... }; }
Expand description
Write hook fn without any proc-macro.
A hook fn is a fn
which returns impl UpdateHookUninitialized
.
(Thus the output type is also UpdateHook
+ IntoHook
).
Tips: cargo fmt
supports formatting code inside hook_fn!(...);
and hook_fn![...];
, but not hook_fn!{...}
.
Usage
hook_fn!(
fn use_constant() -> i32 {
1
}
);
/// `use_constant()` actually returns:
UpdateHookUninitialized![i32]
/// The above macro expands to
impl UpdateHookUninitialized<
Hook = impl Hook + for<'hook> HookValue<'hook, Value = i32>
>
Use other hooks with h!(...)
Usually, you would want to use other hooks in hook_fn.
use hooks::prelude::*;
hook_fn!(
fn use_auto_increment(max: i32) -> i32 {
let state = h!(use_shared_state(0));
let v = state.get();
h!(use_effect_with::<i32, _>(|old_dependency| {
if *old_dependency != Some(v) {
*old_dependency = Some(v);
let state = state.clone();
Some(move |v: &_| state.set(*v + 1))
} else {
None
}
}));
v
}
);
Borrow from arguments
You can borrow from arguments, but you need to declare the lifetimes that this hook borrows from.
use std::rc::Rc;
use hooks::prelude::*;
hook_fn!(
type Bounds = impl 'a;
fn use_lazy_rc<'a, T: Clone + PartialEq>(value: &'a T) -> Rc<T> {
let (rc, _) = h!(use_memo(|value| Rc::new(T::clone(value)), value));
rc.clone()
}
);
There is a limitation that lifetimes must be used in arguments.
Phantom lifetimes will fail to compile.
hook_fn!(
fn use_non_used_lifetime<'a>() -> &'a str {
"hello"
}
);
Use an explicit identifier for hook
h!(use_hook())
actually expands to use_hook().h(hook_id)
,
where hook_id
is of type Pin<&mut UpdateHookUninitialized::Uninitialized>
.
You can use an explicit hook_id
for debugging.
hook_fn!(
fn use_my_state() -> i32 {
println!("{state_hook:?}"); // inspect this hook before it is used
let mut the_state_hook = state_hook;
let state_hook = std::pin::Pin::as_mut(&mut the_state_hook);
let (state, updater) = h![state_hook = use_state(1)];
let state = *state;
println!("{the_state_hook:?}"); // inspect this hook after it is used
state
}
);
Limitations
Only top level token trees are parsed.
The following example doesn’t work because
two macros are in a token tree ()
, which stops them to be parsed.
hook_fn!(
fn use_shared_state_2() -> (i32, i32) {
(
h![use_shared_state(0)].get(),
h![use_shared_state(1)].get(),
)
}
);
You have to make sure h!
to be top level token trees when using hook_fn!
.
hook_fn!(
fn use_shared_state_2() -> (i32, i32) {
let a = h![use_shared_state(0)].get();
let b = h![use_shared_state(1)].get();
(a, b)
}
);
#[hook]
parses any top level expressions (expressions that are not wrapped in {}
).
Thus, the following example works.
#[hook]
fn use_shared_state_2() -> (i32, i32) {
(
use_shared_state(0).get(),
use_shared_state(1).get(),
)
}
Supports at most 10 hooks.
hook_fn!(
fn use_10_hooks() {
h!(use_hook(0)); h!(use_hook(1)); h!(use_hook(2)); h!(use_hook(3)); h!(use_hook(4));
h!(use_hook(5)); h!(use_hook(6)); h!(use_hook(7)); h!(use_hook(8)); h!(use_hook(9));
}
);
hook_fn!(
fn use_11_hooks() {
h!(use_hook(0)); h!(use_hook(1)); h!(use_hook(2)); h!(use_hook(3)); h!(use_hook(4));
h!(use_hook(5)); h!(use_hook(6)); h!(use_hook(7)); h!(use_hook(8)); h!(use_hook(9));
h!(use_hook(10));
}
);
This limitation also applies to #[hook]
because
traits are only implemented for HookTuple<(0, 1, ...)>
with at most 10 elements.
#[hook]
fn use_11_hooks() {
use_hook(0); use_hook(1); use_hook(2); use_hook(3); use_hook(4);
use_hook(5); use_hook(6); use_hook(7); use_hook(8); use_hook(9);
use_hook(10);
}
Output type must not be opaque type (impl Trait)
hook_fn!(
fn use_future() -> impl Future<Output = ()> {
async {}
}
);
#[hook]
allows to do so.
#[hook]
fn use_future() -> impl Future<Output = ()> {
async {}
}