Improve the custom hooks chapter

This commit is contained in:
Evan Almloff committed 2023-04-24 11:16:53 -05:00
1 parent 73a2beb327
commit 61dba2825c
5 files changed
+217 -3

No files matched your search

@@ -2,6 +2,8 @@
Hooks are a great way to encapsulate business logic. If none of the existing hooks work for your problem, you can write your own.
When writing your hook, you can make a function that accepts `cx: &ScopeState` as a parameter to accept a scope with any Props.
## Composing Hooks
To avoid repetition, you can encapsulate business logic based on existing hooks to create a new hook.
@@ -12,6 +14,12 @@ For example, if many components need to access an `AppSettings` struct, you can
{{#include ../../../examples/hooks_composed.rs:wrap_context}}
```
Or if you want to wrap a hook that persists reloads with the storage API, you can build on top of the use_ref hook to work with mutable state:
```rust
{{#include ../../../examples/hooks_composed.rs:use_storage}}
```
## Custom Hook Logic
You can use [`cx.use_hook`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.use_hook) to build your own hooks. In fact, this is what all the standard hooks are built on!
@@ -23,4 +31,61 @@ You can use [`cx.use_hook`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.
Inside the initialization closure, you will typically make calls to other `cx` methods. For example:
- The `use_state` hook tracks state in the hook value, and uses [`cx.schedule_update`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.schedule_update) to make Dioxus re-render the component whenever it changes.
Here is a simplified implementation of the `use_state` hook:
```rust
{{#include ../../../examples/hooks_custom_logic.rs:use_state}}
```
- The `use_context` hook calls [`cx.consume_context`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.consume_context) (which would be expensive to call on every render) to get some context from the scope
Here is an implementation of the `use_context` and `use_context_provider` hooks:
```rust
{{#include ../../../examples/hooks_custom_logic.rs:use_context}}
```
## Hook Anti-Patterns
When writing a custom hook, you should avoid the following anti-patterns:
- !Clone Hooks: To allow hooks to be used within async blocks, the hooks must be Clone. To make a hook clone, you can wrap data in Rc or Arc and avoid lifetimes in hooks.
This version of use_state may seem more efficient, but it is not cloneable:
```rust
{{#include ../../../examples/hooks_anti_patterns.rs:non_clone_state}}
```
If we try to use this hook in an async block, we will get a compile error:
```rust
fn FutureComponent(cx: &ScopeState) -> Element {
let my_state = my_use_state(cx, || 0);
cx.spawn({
to_owned![my_state];
async move {
my_state.set(1);
}
});
todo!()
}
```
But with the original version, we can use it in an async block:
```rust
fn FutureComponent(cx: &ScopeState) -> Element {
let my_state = use_state(cx, || 0);
cx.spawn({
to_owned![my_state];
async move {
my_state.set(1);
}
});
todo!()
}
```