freya/_docs/hooks.rs
1//! # Hooks
2//!
3//! Hooks are special functions that let you tap into Freya's reactivity and lifecycle system.
4//! They can **only** be called at the top level of your component's `render` method (not inside event handlers, loops, or conditionals).
5//!
6//! Hooks are always prefixed with `use_`, for example: [`use_animation`](crate::animation::use_animation), [`use_state`](crate::prelude::use_state), etc.
7//!
8//! ## Rules of Hooks
9//!
10//! Hooks are not ordinary functions. To ensure correct behavior, follow these rules:
11//!
12//! ### 1. Only call hooks at the top level of your `render` method
13//!
14//! Hooks must always be called in the same order on every render.
15//!
16//! ❌ **Incorrect:**
17//! ```rust
18//! # use freya::prelude::*;
19//! #[derive(PartialEq)]
20//! struct MyComponent(bool);
21//! impl Render for MyComponent {
22//! fn render(&self) -> impl IntoElement {
23//! if self.0 {
24//! let state = use_state(|| self.0);
25//! // ...
26//! }
27//! rect()
28//! }
29//! }
30//! ```
31//!
32//! ✅ **Correct:**
33//! ```rust
34//! # use freya::prelude::*;
35//! #[derive(PartialEq)]
36//! struct MyComponent(bool);
37//! impl Render for MyComponent {
38//! fn render(&self) -> impl IntoElement {
39//! let state = use_state(|| self.0);
40//! rect()
41//! }
42//! }
43//! ```
44//!
45//! Or, move state up to the parent component for even simpler code:
46//! ```rust
47//! # use freya::prelude::*;
48//! #[derive(PartialEq)]
49//! struct MyComponent(bool);
50//! impl Render for MyComponent {
51//! fn render(&self) -> impl IntoElement {
52//! rect()
53//! }
54//! }
55//! ```
56//!
57//! ### 2. Only call hooks inside `render` methods
58//!
59//! Hooks **cannot** be called inside event handlers, async blocks, or outside of components.
60//!
61//! ❌ **Incorrect:**
62//! ```rust
63//! # use freya::prelude::*;
64//! struct MyComponent;
65//! impl Render for MyComponent {
66//! fn render(&self) -> impl IntoElement {
67//! let onclick = |_| {
68//! let state = use_state(|| false); // ❌ Not allowed here
69//! };
70//! rect().on_mouse_up(onclick).child("Hello, World!")
71//! }
72//! }
73//! ```
74//!
75//! ✅ **Correct:**
76//! ```rust
77//! # use freya::prelude::*;
78//! struct MyComponent;
79//! impl Render for MyComponent {
80//! fn render(&self) -> impl IntoElement {
81//! let mut state = use_state(|| false);
82//! let onclick = move |_| {
83//! state.set(true);
84//! };
85//! rect().on_mouse_up(onclick).child("Hello, World!")
86//! }
87//! }
88//! ```
89//!
90//! ### 3. Do not call hooks inside loops
91//!
92//! The number of hook calls must not change between renders.
93//!
94//! ❌ **Incorrect:**
95//! ```rust
96//! # use freya::prelude::*;
97//! struct MyComponent;
98//! impl Render for MyComponent {
99//! fn render(&self) -> impl IntoElement {
100//! for i in 0..5 {
101//! let state = use_state(|| i); // ❌ Not allowed in a loop
102//! }
103//! rect().child("Hello, World!")
104//! }
105//! }
106//! ```
107//!
108//! ✅ **Correct:**
109//! ```rust
110//! # use freya::prelude::*;
111//! struct MyComponent;
112//! impl Render for MyComponent {
113//! fn render(&self) -> impl IntoElement {
114//! let state = use_state(|| (0..5).collect::<Vec<_>>());
115//! rect().child("Hello, World!")
116//! }
117//! }
118//! ```