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 Render 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 Render 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 Render 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 {
64 if let Some(value) = CurrentContext::with(|context| {
65 let mut scopes_storages = context.scopes_storages.borrow_mut();
66 let scopes_storage = scopes_storages
67 .get_mut(&context.scope_id)
68 .expect(HOOKS_ERROR);
69 if let Some(value) = scopes_storage
70 .values
71 .get(scopes_storage.current_value)
72 .cloned()
73 {
74 scopes_storage.current_value += 1;
75 Some(value.downcast_ref::<T>().cloned().expect(HOOKS_ERROR))
76 } else if scopes_storage.current_run > 0 {
77 panic!("{HOOKS_ERROR}")
78 } else {
79 None
80 }
81 }) {
82 value
83 } else {
84 let value = init();
85 CurrentContext::with(|context| {
86 let mut scopes_storages = context.scopes_storages.borrow_mut();
87 let scopes_storage = scopes_storages
88 .get_mut(&context.scope_id)
89 .expect(HOOKS_ERROR);
90 scopes_storage.values.push(Rc::new(value.clone()));
91 scopes_storage.current_value += 1;
92 value
93 })
94 }
95}
96
97struct DropInner(Option<Box<dyn FnOnce()>>);
98
99impl std::ops::Drop for DropInner {
100 fn drop(&mut self) {
101 if let Some(f) = self.0.take() {
102 f();
103 }
104 }
105}
106
107pub fn use_drop(drop: impl FnOnce() + 'static) {
108 use_hook(|| Rc::new(DropInner(Some(Box::new(drop)))));
109}
110
111pub fn current_scope_id() -> ScopeId {
112 CurrentContext::with(|context| context.scope_id)
113}
114
115pub fn mark_scope_as_dirty(scope_id: ScopeId) {
116 CurrentContext::with(|context| {
117 context
118 .sender
119 .unbounded_send(Message::MarkScopeAsDirty(scope_id))
120 .unwrap();
121 })
122}