freya_core/lifecycle/
base.rs1use std::rc::Rc;
2
3use crate::{
4 current_context::CurrentContext,
5 runner::Message,
6 scope_id::ScopeId,
7};
8
9static HOOKS_ERROR: &str = "
10Hook functions must follow these rules:
111. You cannot call them conditionally
12
13The following is not allowed and will result in this runtime error.
14
15#[derive(PartialEq)]
16struct CoolComp(u8);
17
18impl Component for CoolComp {
19 fn render(&self) -> impl IntoElement {
20 if self.0 == 2 {
21 let state = use_state(|| 5);
22 }
23
24 rect().into()
25 }
26}
27
282. You cannot call them in for-loops
29
30The following is not allowed and will result in this runtime error.
31
32#[derive(PartialEq)]
33struct CoolComp(u8);
34
35impl Component for CoolComp {
36 fn render(&self) -> impl IntoElement {
37 for i in 0..self.0 {
38 let state = use_state(|| 5);
39 }
40
41 rect().into()
42 }
43}
44
453. You cannot call hooks inside other hooks, event handlers, they should be called in the top of `render` methods from components.
46
47The following is not allowed and will result in this runtime error.
48
49#[derive(PartialEq)]
50struct CoolComp(u8);
51
52impl Component for CoolComp {
53 fn render(&self) -> impl IntoElement {
54 use_side_effect(|| {
55 let state = use_state(|| 5);
56 })
57
58 rect().into()
59 }
60}
61";
62
63pub fn use_hook<T: Clone + 'static>(init: impl FnOnce() -> T) -> T {
76 if let Some(value) = CurrentContext::with(|context| {
77 let mut scopes_storages = context.scopes_storages.borrow_mut();
78 let scopes_storage = scopes_storages
79 .get_mut(&context.scope_id)
80 .expect(HOOKS_ERROR);
81 if let Some(value) = scopes_storage
82 .values
83 .get(scopes_storage.current_value)
84 .cloned()
85 {
86 scopes_storage.current_value += 1;
87 Some(value.downcast_ref::<T>().cloned().expect(HOOKS_ERROR))
88 } else if scopes_storage.current_run > 0 {
89 panic!("{HOOKS_ERROR}")
90 } else {
91 None
92 }
93 }) {
94 value
95 } else {
96 let value = init();
97 CurrentContext::with(|context| {
98 let mut scopes_storages = context.scopes_storages.borrow_mut();
99 let scopes_storage = scopes_storages
100 .get_mut(&context.scope_id)
101 .expect(HOOKS_ERROR);
102 scopes_storage.values.push(Rc::new(value.clone()));
103 scopes_storage.current_value += 1;
104 value
105 })
106 }
107}
108
109struct DropInner(Option<Box<dyn FnOnce()>>);
110
111impl std::ops::Drop for DropInner {
112 fn drop(&mut self) {
113 if let Some(f) = self.0.take() {
114 f();
115 }
116 }
117}
118
119pub fn use_drop(drop: impl FnOnce() + 'static) {
127 use_hook(|| Rc::new(DropInner(Some(Box::new(drop)))));
128}
129
130pub fn current_scope_id() -> ScopeId {
131 CurrentContext::with(|context| context.scope_id)
132}
133
134pub fn mark_scope_as_dirty(scope_id: ScopeId) {
135 CurrentContext::with(|context| {
136 context
137 .sender
138 .unbounded_send(Message::MarkScopeAsDirty(scope_id))
139 .unwrap();
140 })
141}