gpui_hooks/hooks/
use_effect.rs1use super::{Dependency, Hook};
2use std::any::Any;
3use std::cell::RefCell;
4
5pub struct UseEffect {
7 deps: Vec<Box<dyn Dependency>>,
8 cleanup: Option<Box<dyn FnOnce()>>,
9 has_run: bool,
10}
11
12impl UseEffect {
13 pub fn new(deps: Vec<Box<dyn Dependency>>) -> Self {
14 Self {
15 deps,
16 cleanup: None,
17 has_run: false,
18 }
19 }
20
21 pub fn deps_changed(&self, new_deps: &[Box<dyn Dependency>]) -> bool {
23 if !self.has_run {
24 return true;
25 }
26 if self.deps.len() != new_deps.len() {
27 return true;
28 }
29 self.deps
30 .iter()
31 .zip(new_deps.iter())
32 .any(|(old, new)| !old.equals(new.as_ref()))
33 }
34
35 pub fn update_deps(&mut self, new_deps: Vec<Box<dyn Dependency>>) {
37 self.deps = new_deps;
38 }
39
40 pub fn mark_run(&mut self) {
42 self.has_run = true;
43 }
44
45 pub fn set_cleanup(&mut self, cleanup: Box<dyn FnOnce()>) {
47 if let Some(prev_cleanup) = self.cleanup.take() {
49 prev_cleanup();
50 }
51 self.cleanup = Some(cleanup);
52 }
53
54 pub fn run_cleanup(&mut self) {
56 if let Some(cleanup) = self.cleanup.take() {
57 cleanup();
58 }
59 }
60}
61
62impl Hook for UseEffect {
63 fn as_any(&self) -> &dyn Any {
64 self
65 }
66 fn as_any_mut(&mut self) -> &mut dyn Any {
67 self
68 }
69}
70
71pub trait UseEffectHook {
76 fn _hooks_ref(&self) -> &RefCell<Vec<Box<dyn Hook>>>;
78
79 fn _next_hook_index(&self) -> usize;
81
82 fn use_effect<F, C, D>(&self, effect: F, deps: D)
86 where
87 F: FnOnce() -> Option<C>,
88 C: FnOnce() + 'static,
89 D: IntoIterator<Item: Dependency + Clone>,
90 {
91 let idx = self._next_hook_index();
92 let hooks_ref = self._hooks_ref();
93
94 let boxed_deps: Vec<Box<dyn Dependency>> = deps
96 .into_iter()
97 .map(|d| Box::new(d.clone()) as Box<dyn Dependency>)
98 .collect();
99
100 let hooks_len = hooks_ref.borrow().len();
102 if idx >= hooks_len {
103 let mut effect_hook = UseEffect::new(boxed_deps.clone());
104
105 if let Some(cleanup) = effect() {
107 effect_hook.set_cleanup(Box::new(cleanup));
108 }
109 effect_hook.mark_run();
110
111 hooks_ref.borrow_mut().push(Box::new(effect_hook));
112 } else {
113 let mut hooks = hooks_ref.borrow_mut();
114 let hook = hooks.get_mut(idx).expect("Hook index out of bounds");
115 let effect_hook = hook
116 .as_any_mut()
117 .downcast_mut::<UseEffect>()
118 .expect("Hook type mismatch at index");
119
120 if effect_hook.deps_changed(&boxed_deps) {
121 effect_hook.run_cleanup();
123
124 effect_hook.update_deps(boxed_deps);
126
127 if let Some(cleanup) = effect() {
129 effect_hook.set_cleanup(Box::new(cleanup));
130 }
131 effect_hook.mark_run();
132 }
133 }
134 }
135
136 fn cleanup_effects(&self) {
138 for hook in self._hooks_ref().borrow_mut().iter_mut() {
139 if let Some(effect) = hook.as_any_mut().downcast_mut::<UseEffect>() {
140 effect.run_cleanup();
141 }
142 }
143 }
144}