44 个版本
新 3.0.0-alpha.15 | 2024 年 8 月 21 日 |
---|---|
3.0.0-alpha.13 | 2024 年 7 月 29 日 |
3.0.0-alpha.10 | 2024 年 6 月 19 日 |
3.0.0-alpha.3 | 2024 年 2 月 20 日 |
1.0.0-alpha.18 | 2022 年 11 月 23 日 |
#173 in Rust 模式
435 每月下载量
用于 hooks-yew
245KB
6K SLoC
::hooks
在安全 Rust 中的编译时、异步钩子。
快速入门
运行 cargo add hooks
将 hooks
添加到您的项目中。
注意,该项目仍在 alpha 阶段,可能会有重大更改。
在升级之前,请参阅 变更日志。
您可以使用 hook_fn!(...)
或 #[hook]
在钩子 fn 中组合钩子。
使用 hook_fn!(...)
use hooks::prelude::*;
hook_fn!(
fn use_demo() {
let (state, updater) = h![hooks::use_shared_set(0)];
let updater = updater.clone();
h![hooks::use_effect(move |v: &i32| {
println!("state = {}", *v);
if *v < 2 {
updater.set(*v + 1);
}
}, *state)];
}
);
futures_lite::future::block_on(async {
let mut hook = use_demo().into_hook();
while let Some(()) = hook.next_value().await {}
});
使用 #[hook]
此属性宏仅在 proc-macro
功能下可用。使用 cargo add -p hook --features proc-macro
启用它。 #[hook]
允许使用钩子而不需要 h!()
。任何以 use_
开头的函数调用或方法调用都会自动检测为钩子。
use hooks::prelude::*;
#[hook]
fn use_demo() {
let (state, updater) = hooks::use_shared_set(0);
let updater = updater.clone();
hooks::use_effect(move |v: &i32| {
println!("state = {}", *v);
if *v < 2 {
updater.set(*v + 1);
}
}, *state);
}
fn main() {
futures_lite::future::block_on(async {
let mut hook = use_demo().into_hook();
while let Some(()) = hook.next_value().await {}
});
}
您将看到以下日志。然后程序优雅地退出,因为它知道不会有新的值。
state = 0
state = 1
state = 2
什么是编译时的 Hook
?
为了理解这个crate背后的概念,您可以展开此部分并阅读详细内容。
React 16.8中引入的Hooks,是一种将状态引入函数组件的方法。Hooks可以使无状态的函数组件有状态和响应式。
传统的Hooks实现使用全局状态来记录hook调用及其状态。这样,无状态的函数可以通过运行时上下文来保持其状态。因此,hook调用的顺序不能改变;也禁止条件hook调用。开发者必须遵循Hooks规则
来编写有效的自定义hook。yew.rs
还向使用的hook传递hook上下文。我们可以看到上述实现依赖于hook fn的运行时行为。hook运行器必须运行该函数一次以知道初始化了什么。我们称这种hook为运行时Hooks。
Rust语言拥有强大的静态类型系统。实际上,hook函数的状态是静态类型的。难题是如何使无状态的函数有状态,这意味着其状态也应由执行器知道。我们称这种hook实现为编译时Hooks。
此crate为您定义和实现了编译时Hooks。
当类型实现Hook
时,它定义了三种行为
-
使用此hook时,它会输出什么?
Hook::use_hook
返回HookValue::Value
。此crate使用GAT (泛型关联类型)允许输出类型从hook本身借用。由于一些真实GAT的限制,此crate使用由Sabrina Jewson引入的更好的GAT模式。感谢她!
-
我们应该何时重用此hook?
Hooks有状态。当状态不改变时,我们不需要重新调用
use_hook
来获取新的输出。我们可以等待hook的状态改变,使用HookPollNextUpdate::poll_next_update
,或者简单地通过hook.next_update().await
。要等待状态改变后的下一个值,可以使用
hook.next_value().await
方法。
如何编写自定义hook?
请参阅Hook
特质。
如何在hook函数中使用hooks?
使用宏 hook_fn!
,你可以在顶级标记树(不是像()
、[]
或{}
那样的标记树中)中使用h![use_another_hook(arg0, arg1)]
。该宏将转换调用。
使用宏 #[hook]
,你可以在顶级表达式(不在像{}
这样的内部块中)中调用use_another_hook(arg0, arg1)
。该宏将转换调用。你可以查看此宏输出的快照。
如何有条件地使用钩子?
请参阅 use_lazy_pinned_hook
和 use_uninitialized_hook
。
如何在不是钩子函数的情况下使用钩子
钩子函数实际上返回 impl UpdateHookUninitialized
。要使用它,你可以运行 use_my_hook().into_hook()
将它转换为 Hook
,或者运行 use_my_hook().into_hook_values()
(它运行 use_my_hook().into_hook().into_values()
)来获取异步迭代值。
要消费 Hook
,你可以使用其下一个值与 hook.next_value().await
。你可以使用 hook.values()
或 hook.into_values()
(如果钩子是 NonLendingHook
,则它是一个 Stream
)来获取异步迭代值。
# use hooks::prelude::*;
hook_fn!(
fn use_demo() -> i32 {
let (state, updater) = h![use_shared_call(
0,
|v| {
if *v < 2 {
*v += 1;
true // indicating state is updated
} else {
false // indicating state is not updated
}
},
)];
let updater = updater.clone();
h![hooks::use_effect(move |_: &i32| {
updater.call();
}, *state)];
*state
}
);
// with hook.next_value().await
futures_lite::future::block_on(async {
let mut hook = use_demo().into_hook();
assert_eq!(hook.next_value().await, Some(0));
assert_eq!(hook.next_value().await, Some(1));
assert_eq!(hook.next_value().await, Some(2));
assert_eq!(hook.next_value().await, None);
});
// with hook.into_hook_values() and stream.next().await
futures_lite::future::block_on(async {
use futures_lite::StreamExt;
let mut values = use_demo().into_hook_values();
assert_eq!(values.next().await, Some(0));
assert_eq!(values.next().await, Some(1));
assert_eq!(values.next().await, Some(2));
assert_eq!(values.next().await, None);
});
// with hook.into_hook_values() and stream.collect().await
futures_lite::future::block_on(async {
use futures_lite::StreamExt;
let values = use_demo().into_hook_values();
let values = values.collect::<Vec<_>>().await;
assert_eq!(values, [0, 1, 2]);
});
依赖项
~115–315KB