Muhammad Hamza f0209c786f
Prepare for Yew 0.20 (#2973)
* 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>
2022-11-25 15:19:07 +05:00

122 lines
4.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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
}
```