mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
* 0.20 Changelog * Improve changelog generator * Add blog post * Add blog post * Apply suggestions from code review Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com> Co-authored-by: Julius Lungys <32368314+voidpumpkin@users.noreply.github.com> * update Changelog * update Cargo.toml * changelog gen compiles * website version 0.20 * add migration guides * prettier * i18n Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com> Co-authored-by: Julius Lungys <32368314+voidpumpkin@users.noreply.github.com>
122 lines
4.2 KiB
Plaintext
122 lines
4.2 KiB
Plaintext
---
|
||
title: 自定义钩子(Custom Hooks)
|
||
description: 定义你自己的 Hooks
|
||
---
|
||
|
||
## 定义自定义钩子
|
||
|
||
组件中与状态有关的逻辑可以通过创建自定义 Hooks 提取到函数中。
|
||
|
||
假设我们有一个组件,它订阅了一个代理(agent)并且会显示发送给它的消息。
|
||
|
||
```rust
|
||
#[function_component(ShowMessages)]
|
||
pub fn show_messages() -> Html {
|
||
let (state, set_state) = use_state(|| vec![]);
|
||
|
||
{
|
||
let mut state = Rc::clone(&state);
|
||
use_effect(move || {
|
||
let producer = EventBus::bridge(Callback::from(move |msg| {
|
||
let mut messages = (*state).clone();
|
||
messages.push(msg);
|
||
set_state(messages)
|
||
}));
|
||
|
||
|| drop(producer)
|
||
});
|
||
}
|
||
|
||
let output = state.iter().map(|it| html! { <p>{ it }</p> });
|
||
html! { <div>{ for output }</div> }
|
||
}
|
||
```
|
||
|
||
这段代码有一个问题:逻辑不能被另一个组件重用。如果我们构建另一个跟踪消息的组件,我们可以将逻辑移动到自定义钩子中,而不是复制代码。
|
||
|
||
我们将首先创建一个名为`use_subscribe`的新函数。 `use_`前缀通常表示此函数是一个钩子。这个函数将不接受任何参数并返回`Rc<RefCell<Vec<String>>>` 。
|
||
|
||
```rust
|
||
fn use_subscribe() -> Rc<RefCell<Vec<String>>> {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
钩子的逻辑在`use_hook`的回调中。 `use_hook`指的是自定义 Hook 的处理函数。它接受 2 个参数: `hook_runner`和`initial_state_producer` 。
|
||
|
||
`hook_runner`中包含了所有钩子的逻辑,它的回调的返回值又会被`use_hook`返回。 `hook_runner`需要 2 个参数:分别是对钩子和`hook_callback`它们两个的内部状态的可变引用。 而`hook_callback`同样也要 2 个参数:一个回调和一个 bool,回调接受`internal_state` ,也就是对内部状态实例的可变引用,并且会调执行实际的更改,还会返回表示`ShouldRender`的布尔值,第二个参数 bool 的用处是指示它是否在组件渲染后运行。`use_hook`的第二个参数`initial_state_producer`接受用于创建内部状态实例的回调。这里说的内部状态指的是一个实现了`Hook` trait 的结构体。
|
||
|
||
现在让我们为`use_subscribe`钩子创建状态(state struct)。
|
||
|
||
```rust
|
||
/// `use_subscribe` internal state
|
||
struct UseSubscribeState {
|
||
/// holds all the messages received
|
||
pub messages: Rc<RefCell<Vec<String>>>,
|
||
}
|
||
|
||
impl Hook for UseSubscribeState {}
|
||
```
|
||
|
||
接下来我们为`use_subscribe`添加实际逻辑。
|
||
|
||
```rust
|
||
fn use_subscribe() -> Rc<RefCell<Vec<String>>> {
|
||
use_hook(
|
||
// hook's handler. all the logic goes in here
|
||
|state: &mut UseSubscribeState, hook_callback| {
|
||
// calling other Hooks inside a hook
|
||
use_effect(move || {
|
||
let producer = EventBus::bridge(Callback::from(move |msg| {
|
||
hook_callback(
|
||
// where the mutations of state are performed
|
||
|state| {
|
||
(*state.messages).borrow_mut().deref_mut().push(msg);
|
||
true // should re-render
|
||
}, false // run post-render
|
||
)
|
||
}));
|
||
|
||
|| drop(producer)
|
||
});
|
||
|
||
// return from hook
|
||
state.messages.clone()
|
||
},
|
||
// initial state producer
|
||
|| UseSubscribeState { messages: Rc::new(RefCell::new(vec![])) },
|
||
)
|
||
}
|
||
```
|
||
|
||
现在我们可以使用自定义钩子了:
|
||
|
||
```rust
|
||
#[function_component(ShowMessages)]
|
||
pub fn show_messages() -> Html {
|
||
let state = use_subscribe();
|
||
let output = state.borrow().deref().into_iter().map(|it| html! { <p>{ it }</p> });
|
||
|
||
html! { <div>{ for output }</div> }
|
||
}
|
||
```
|
||
|
||
需要特别注意的是创建自定义钩子时`use_hook`不是必须的,它们只是用来包含其他钩子。通常应避免使用`use_hook`。
|
||
|
||
```rust
|
||
fn use_subscribe() -> Rc<Vec<String>> {
|
||
let (state, set_state) = use_state(Vec::new);
|
||
|
||
use_effect(move || {
|
||
let producer = EventBus::bridge(Callback::from(move |msg| {
|
||
let mut messages = (*state).clone();
|
||
messages.push(msg);
|
||
set_state(messages)
|
||
}));
|
||
|| drop(producer)
|
||
});
|
||
|
||
state
|
||
}
|
||
```
|