scopes.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. use crate::{
  2. any_props::AnyProps,
  3. any_props::VProps,
  4. bump_frame::BumpFrame,
  5. innerlude::{DynamicNode, EventHandler, VComponent, VText},
  6. innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
  7. lazynodes::LazyNodes,
  8. nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
  9. AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId,
  10. };
  11. use bumpalo::{boxed::Box as BumpBox, Bump};
  12. use bumpslab::{BumpSlab, Slot};
  13. use rustc_hash::FxHashSet;
  14. use slab::{Slab, VacantEntry};
  15. use std::{
  16. any::{Any, TypeId},
  17. cell::{Cell, RefCell, UnsafeCell},
  18. fmt::{Arguments, Debug},
  19. future::Future,
  20. ops::{Index, IndexMut},
  21. rc::Rc,
  22. sync::Arc,
  23. };
  24. /// A wrapper around the [`Scoped`] object that contains a reference to the [`ScopeState`] and properties for a given
  25. /// component.
  26. ///
  27. /// The [`Scope`] is your handle to the [`crate::VirtualDom`] and the component state. Every component is given its own
  28. /// [`ScopeState`] and merged with its properties to create a [`Scoped`].
  29. ///
  30. /// The [`Scope`] handle specifically exists to provide a stable reference to these items for the lifetime of the
  31. /// component render.
  32. pub type Scope<'a, T = ()> = &'a Scoped<'a, T>;
  33. // This ScopedType exists because we want to limit the amount of monomorphization that occurs when making inner
  34. // state type generic over props. When the state is generic, it causes every method to be monomorphized for every
  35. // instance of Scope<T> in the codebase.
  36. //
  37. //
  38. /// A wrapper around a component's [`ScopeState`] and properties. The [`ScopeState`] provides the majority of methods
  39. /// for the VirtualDom and component state.
  40. pub struct Scoped<'a, T = ()> {
  41. /// The component's state and handle to the scheduler.
  42. ///
  43. /// Stores things like the custom bump arena, spawn functions, hooks, and the scheduler.
  44. pub scope: &'a ScopeState,
  45. /// The component's properties.
  46. pub props: &'a T,
  47. }
  48. impl<'a, T> std::ops::Deref for Scoped<'a, T> {
  49. type Target = &'a ScopeState;
  50. fn deref(&self) -> &Self::Target {
  51. &self.scope
  52. }
  53. }
  54. /// A component's unique identifier.
  55. ///
  56. /// `ScopeId` is a `usize` that acts a key for the internal slab of Scopes. This means that the key is not unqiue across
  57. /// time. We do try and guarantee that between calls to `wait_for_work`, no ScopeIds will be recycled in order to give
  58. /// time for any logic that relies on these IDs to properly update.
  59. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  60. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
  61. pub struct ScopeId(pub usize);
  62. /// A thin wrapper around a BumpSlab that uses ids to index into the slab.
  63. pub(crate) struct ScopeSlab {
  64. slab: BumpSlab<ScopeState>,
  65. // a slab of slots of stable pointers to the ScopeState in the bump slab
  66. entries: Slab<Slot<'static, ScopeState>>,
  67. }
  68. impl Drop for ScopeSlab {
  69. fn drop(&mut self) {
  70. // Bump slab doesn't drop its contents, so we need to do it manually
  71. for slot in self.entries.drain() {
  72. self.slab.remove(slot);
  73. }
  74. }
  75. }
  76. impl Default for ScopeSlab {
  77. fn default() -> Self {
  78. Self {
  79. slab: BumpSlab::new(),
  80. entries: Slab::new(),
  81. }
  82. }
  83. }
  84. impl ScopeSlab {
  85. pub(crate) fn get(&self, id: ScopeId) -> Option<&ScopeState> {
  86. self.entries.get(id.0).map(|slot| unsafe { &*slot.ptr() })
  87. }
  88. pub(crate) fn get_mut(&mut self, id: ScopeId) -> Option<&mut ScopeState> {
  89. self.entries
  90. .get(id.0)
  91. .map(|slot| unsafe { &mut *slot.ptr_mut() })
  92. }
  93. pub(crate) fn vacant_entry(&mut self) -> ScopeSlabEntry {
  94. let entry = self.entries.vacant_entry();
  95. ScopeSlabEntry {
  96. slab: &mut self.slab,
  97. entry,
  98. }
  99. }
  100. pub(crate) fn remove(&mut self, id: ScopeId) {
  101. self.slab.remove(self.entries.remove(id.0));
  102. }
  103. pub(crate) fn contains(&self, id: ScopeId) -> bool {
  104. self.entries.contains(id.0)
  105. }
  106. pub(crate) fn iter(&self) -> impl Iterator<Item = &ScopeState> {
  107. self.entries.iter().map(|(_, slot)| unsafe { &*slot.ptr() })
  108. }
  109. }
  110. pub(crate) struct ScopeSlabEntry<'a> {
  111. slab: &'a mut BumpSlab<ScopeState>,
  112. entry: VacantEntry<'a, Slot<'static, ScopeState>>,
  113. }
  114. impl<'a> ScopeSlabEntry<'a> {
  115. pub(crate) fn key(&self) -> ScopeId {
  116. ScopeId(self.entry.key())
  117. }
  118. pub(crate) fn insert(self, scope: ScopeState) -> &'a ScopeState {
  119. let slot = self.slab.push(scope);
  120. // this is safe because the slot is only ever accessed with the lifetime of the borrow of the slab
  121. let slot = unsafe { std::mem::transmute(slot) };
  122. let entry = self.entry.insert(slot);
  123. unsafe { &*entry.ptr() }
  124. }
  125. }
  126. impl Index<ScopeId> for ScopeSlab {
  127. type Output = ScopeState;
  128. fn index(&self, id: ScopeId) -> &Self::Output {
  129. self.get(id).unwrap()
  130. }
  131. }
  132. impl IndexMut<ScopeId> for ScopeSlab {
  133. fn index_mut(&mut self, id: ScopeId) -> &mut Self::Output {
  134. self.get_mut(id).unwrap()
  135. }
  136. }
  137. /// A component's state separate from its props.
  138. ///
  139. /// This struct exists to provide a common interface for all scopes without relying on generics.
  140. pub struct ScopeState {
  141. pub(crate) render_cnt: Cell<usize>,
  142. pub(crate) name: &'static str,
  143. pub(crate) node_arena_1: BumpFrame,
  144. pub(crate) node_arena_2: BumpFrame,
  145. pub(crate) parent: Option<*const ScopeState>,
  146. pub(crate) id: ScopeId,
  147. pub(crate) height: u32,
  148. pub(crate) suspended: Cell<bool>,
  149. pub(crate) hooks: RefCell<Vec<Box<UnsafeCell<dyn Any>>>>,
  150. pub(crate) hook_idx: Cell<usize>,
  151. pub(crate) shared_contexts: RefCell<Vec<(TypeId, Box<dyn Any>)>>,
  152. pub(crate) tasks: Rc<Scheduler>,
  153. pub(crate) spawned_tasks: RefCell<FxHashSet<TaskId>>,
  154. pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
  155. pub(crate) attributes_to_drop: RefCell<Vec<*const Attribute<'static>>>,
  156. pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
  157. }
  158. impl<'src> ScopeState {
  159. pub(crate) fn current_frame(&self) -> &BumpFrame {
  160. match self.render_cnt.get() % 2 {
  161. 0 => &self.node_arena_1,
  162. 1 => &self.node_arena_2,
  163. _ => unreachable!(),
  164. }
  165. }
  166. pub(crate) fn previous_frame(&self) -> &BumpFrame {
  167. match self.render_cnt.get() % 2 {
  168. 1 => &self.node_arena_1,
  169. 0 => &self.node_arena_2,
  170. _ => unreachable!(),
  171. }
  172. }
  173. /// Get the name of this component
  174. pub fn name(&self) -> &str {
  175. self.name
  176. }
  177. /// Get the current render since the inception of this component
  178. ///
  179. /// This can be used as a helpful diagnostic when debugging hooks/renders, etc
  180. pub fn generation(&self) -> usize {
  181. self.render_cnt.get()
  182. }
  183. /// Get a handle to the currently active bump arena for this Scope
  184. ///
  185. /// This is a bump memory allocator. Be careful using this directly since the contents will be wiped on the next render.
  186. /// It's easy to leak memory here since the drop implementation will not be called for any objects allocated in this arena.
  187. ///
  188. /// If you need to allocate items that need to be dropped, use bumpalo's box.
  189. pub fn bump(&self) -> &Bump {
  190. // note that this is actually the previous frame since we use that as scratch space while the component is rendering
  191. self.previous_frame().bump()
  192. }
  193. /// Get a handle to the currently active head node arena for this Scope
  194. ///
  195. /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
  196. ///
  197. /// Panics if the tree has not been built yet.
  198. pub fn root_node(&self) -> &RenderReturn {
  199. self.try_root_node()
  200. .expect("The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.")
  201. }
  202. /// Try to get a handle to the currently active head node arena for this Scope
  203. ///
  204. /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
  205. ///
  206. /// Returns [`None`] if the tree has not been built yet.
  207. pub fn try_root_node(&self) -> Option<&RenderReturn> {
  208. let ptr = self.current_frame().node.get();
  209. if ptr.is_null() {
  210. return None;
  211. }
  212. let r: &RenderReturn = unsafe { &*ptr };
  213. unsafe { std::mem::transmute(r) }
  214. }
  215. /// Get the height of this Scope - IE the number of scopes above it.
  216. ///
  217. /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
  218. ///
  219. /// # Example
  220. ///
  221. /// ```rust, ignore
  222. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  223. /// dom.rebuild();
  224. ///
  225. /// let base = dom.base_scope();
  226. ///
  227. /// assert_eq!(base.height(), 0);
  228. /// ```
  229. pub fn height(&self) -> u32 {
  230. self.height
  231. }
  232. /// Get the Parent of this [`Scope`] within this Dioxus [`crate::VirtualDom`].
  233. ///
  234. /// This ID is not unique across Dioxus [`crate::VirtualDom`]s or across time. IDs will be reused when components are unmounted.
  235. ///
  236. /// The base component will not have a parent, and will return `None`.
  237. ///
  238. /// # Example
  239. ///
  240. /// ```rust, ignore
  241. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  242. /// dom.rebuild();
  243. ///
  244. /// let base = dom.base_scope();
  245. ///
  246. /// assert_eq!(base.parent(), None);
  247. /// ```
  248. pub fn parent(&self) -> Option<ScopeId> {
  249. // safety: the pointer to our parent is *always* valid thanks to the bump arena
  250. self.parent.map(|p| unsafe { &*p }.id)
  251. }
  252. /// Get the ID of this Scope within this Dioxus [`crate::VirtualDom`].
  253. ///
  254. /// This ID is not unique across Dioxus [`crate::VirtualDom`]s or across time. IDs will be reused when components are unmounted.
  255. ///
  256. /// # Example
  257. ///
  258. /// ```rust, ignore
  259. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  260. /// dom.rebuild();
  261. /// let base = dom.base_scope();
  262. ///
  263. /// assert_eq!(base.scope_id(), 0);
  264. /// ```
  265. pub fn scope_id(&self) -> ScopeId {
  266. self.id
  267. }
  268. /// Create a subscription that schedules a future render for the reference component
  269. ///
  270. /// ## Notice: you should prefer using [`Self::schedule_update_any`] and [`Self::scope_id`]
  271. pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
  272. let (chan, id) = (self.tasks.sender.clone(), self.scope_id());
  273. Arc::new(move || drop(chan.unbounded_send(SchedulerMsg::Immediate(id))))
  274. }
  275. /// Schedule an update for any component given its [`ScopeId`].
  276. ///
  277. /// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
  278. ///
  279. /// This method should be used when you want to schedule an update for a component
  280. pub fn schedule_update_any(&self) -> Arc<dyn Fn(ScopeId) + Send + Sync> {
  281. let chan = self.tasks.sender.clone();
  282. Arc::new(move |id| {
  283. chan.unbounded_send(SchedulerMsg::Immediate(id)).unwrap();
  284. })
  285. }
  286. /// Mark this scope as dirty, and schedule a render for it.
  287. pub fn needs_update(&self) {
  288. self.needs_update_any(self.scope_id());
  289. }
  290. /// Get the [`ScopeId`] of a mounted component.
  291. ///
  292. /// `ScopeId` is not unique for the lifetime of the [`crate::VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted.
  293. pub fn needs_update_any(&self, id: ScopeId) {
  294. self.tasks
  295. .sender
  296. .unbounded_send(SchedulerMsg::Immediate(id))
  297. .expect("Scheduler to exist if scope exists");
  298. }
  299. /// Return any context of type T if it exists on this scope
  300. pub fn has_context<T: 'static + Clone>(&self) -> Option<T> {
  301. self.shared_contexts
  302. .borrow()
  303. .iter()
  304. .find(|(k, _)| *k == TypeId::of::<T>())
  305. .map(|(_, v)| v)?
  306. .downcast_ref::<T>()
  307. .cloned()
  308. }
  309. /// Try to retrieve a shared state with type `T` from any parent scope.
  310. ///
  311. /// Clones the state if it exists.
  312. pub fn consume_context<T: 'static + Clone>(&self) -> Option<T> {
  313. if let Some(this_ctx) = self.has_context() {
  314. return Some(this_ctx);
  315. }
  316. let mut search_parent = self.parent;
  317. while let Some(parent_ptr) = search_parent {
  318. // safety: all parent pointers are valid thanks to the bump arena
  319. let parent = unsafe { &*parent_ptr };
  320. if let Some(shared) = parent
  321. .shared_contexts
  322. .borrow()
  323. .iter()
  324. .find(|(k, _)| *k == TypeId::of::<T>())
  325. {
  326. return shared.1.downcast_ref::<T>().cloned();
  327. }
  328. search_parent = parent.parent;
  329. }
  330. None
  331. }
  332. /// Expose state to children further down the [`crate::VirtualDom`] Tree. Requires `Clone` on the context to allow getting values down the tree.
  333. ///
  334. /// This is a "fundamental" operation and should only be called during initialization of a hook.
  335. ///
  336. /// For a hook that provides the same functionality, use `use_provide_context` and `use_context` instead.
  337. ///
  338. /// # Example
  339. ///
  340. /// ```rust, ignore
  341. /// struct SharedState(&'static str);
  342. ///
  343. /// static App: Component = |cx| {
  344. /// cx.use_hook(|| cx.provide_context(SharedState("world")));
  345. /// render!(Child {})
  346. /// }
  347. ///
  348. /// static Child: Component = |cx| {
  349. /// let state = cx.consume_state::<SharedState>();
  350. /// render!(div { "hello {state.0}" })
  351. /// }
  352. /// ```
  353. pub fn provide_context<T: 'static + Clone>(&self, value: T) -> T {
  354. let mut contexts = self.shared_contexts.borrow_mut();
  355. // If the context exists, swap it out for the new value
  356. for ctx in contexts.iter_mut() {
  357. // Swap the ptr directly
  358. if let Some(ctx) = ctx.1.downcast_mut::<T>() {
  359. std::mem::swap(ctx, &mut value.clone());
  360. return value;
  361. }
  362. }
  363. // Else, just push it
  364. contexts.push((TypeId::of::<T>(), Box::new(value.clone())));
  365. value
  366. }
  367. /// Provide a context to the root and then consume it
  368. ///
  369. /// This is intended for "global" state management solutions that would rather be implicit for the entire app.
  370. /// Things like signal runtimes and routers are examples of "singletons" that would benefit from lazy initialization.
  371. ///
  372. /// Note that you should be checking if the context existed before trying to provide a new one. Providing a context
  373. /// when a context already exists will swap the context out for the new one, which may not be what you want.
  374. pub fn provide_root_context<T: 'static + Clone>(&self, context: T) -> T {
  375. let mut parent = self;
  376. // Walk upwards until there is no more parent - and tada we have the root
  377. while let Some(next_parent) = parent.parent {
  378. parent = unsafe { &*next_parent };
  379. debug_assert_eq!(parent.scope_id(), ScopeId(0));
  380. }
  381. parent.provide_context(context)
  382. }
  383. /// Pushes the future onto the poll queue to be polled after the component renders.
  384. pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
  385. let id = self.tasks.spawn(self.id, fut);
  386. self.spawned_tasks.borrow_mut().insert(id);
  387. id
  388. }
  389. /// Spawns the future but does not return the [`TaskId`]
  390. pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) {
  391. self.push_future(fut);
  392. }
  393. /// Spawn a future that Dioxus won't clean up when this component is unmounted
  394. ///
  395. /// This is good for tasks that need to be run after the component has been dropped.
  396. pub fn spawn_forever(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
  397. // The root scope will never be unmounted so we can just add the task at the top of the app
  398. let id = self.tasks.spawn(ScopeId(0), fut);
  399. // wake up the scheduler if it is sleeping
  400. self.tasks
  401. .sender
  402. .unbounded_send(SchedulerMsg::TaskNotified(id))
  403. .expect("Scheduler should exist");
  404. self.spawned_tasks.borrow_mut().insert(id);
  405. id
  406. }
  407. /// Informs the scheduler that this task is no longer needed and should be removed.
  408. ///
  409. /// This drops the task immediately.
  410. pub fn remove_future(&self, id: TaskId) {
  411. self.tasks.remove(id);
  412. }
  413. /// Take a lazy [`crate::VNode`] structure and actually build it with the context of the efficient [`bumpalo::Bump`] allocator.
  414. ///
  415. /// ## Example
  416. ///
  417. /// ```ignore
  418. /// fn Component(cx: Scope<Props>) -> Element {
  419. /// // Lazy assemble the VNode tree
  420. /// let lazy_nodes = rsx!("hello world");
  421. ///
  422. /// // Actually build the tree and allocate it
  423. /// cx.render(lazy_tree)
  424. /// }
  425. ///```
  426. pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
  427. let element = rsx.call(self);
  428. let mut listeners = self.attributes_to_drop.borrow_mut();
  429. for attr in element.dynamic_attrs {
  430. match attr.value {
  431. AttributeValue::Any(_) | AttributeValue::Listener(_) => {
  432. let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
  433. listeners.push(unbounded);
  434. }
  435. _ => (),
  436. }
  437. }
  438. let mut props = self.borrowed_props.borrow_mut();
  439. for node in element.dynamic_nodes {
  440. if let DynamicNode::Component(comp) = node {
  441. if !comp.static_props {
  442. let unbounded = unsafe { std::mem::transmute(comp as *const VComponent) };
  443. props.push(unbounded);
  444. }
  445. }
  446. }
  447. Some(element)
  448. }
  449. /// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
  450. pub fn text_node(&'src self, args: Arguments) -> DynamicNode<'src> {
  451. DynamicNode::Text(VText {
  452. value: self.raw_text(args),
  453. id: Default::default(),
  454. })
  455. }
  456. /// Allocate some text inside the [`ScopeState`] from [`Arguments`]
  457. ///
  458. /// Uses the currently active [`Bump`] allocator
  459. pub fn raw_text(&'src self, args: Arguments) -> &'src str {
  460. args.as_str().unwrap_or_else(|| {
  461. use bumpalo::core_alloc::fmt::Write;
  462. let mut str_buf = bumpalo::collections::String::new_in(self.bump());
  463. str_buf.write_fmt(args).unwrap();
  464. str_buf.into_bump_str()
  465. })
  466. }
  467. /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`] using the internal [`Bump`] allocator
  468. pub fn make_node<'c, I>(&'src self, into: impl IntoDynNode<'src, I> + 'c) -> DynamicNode {
  469. into.into_vnode(self)
  470. }
  471. /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
  472. ///
  473. /// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
  474. /// some renderers stay in sync with the VirtualDom's understanding of the world
  475. pub fn attr(
  476. &'src self,
  477. name: &'static str,
  478. value: impl IntoAttributeValue<'src>,
  479. namespace: Option<&'static str>,
  480. volatile: bool,
  481. ) -> Attribute<'src> {
  482. Attribute {
  483. name,
  484. namespace,
  485. volatile,
  486. mounted_element: Default::default(),
  487. value: value.into_value(self.bump()),
  488. }
  489. }
  490. /// Create a new [`DynamicNode::Component`] variant
  491. ///
  492. ///
  493. /// The given component can be any of four signatures. Remember that an [`Element`] is really a [`Result<VNode>`].
  494. ///
  495. /// ```rust, ignore
  496. /// // Without explicit props
  497. /// fn(Scope) -> Element;
  498. /// async fn(Scope<'_>) -> Element;
  499. ///
  500. /// // With explicit props
  501. /// fn(Scope<Props>) -> Element;
  502. /// async fn(Scope<Props<'_>>) -> Element;
  503. /// ```
  504. pub fn component<P>(
  505. &'src self,
  506. component: fn(Scope<'src, P>) -> Element<'src>,
  507. props: P,
  508. fn_name: &'static str,
  509. ) -> DynamicNode<'src>
  510. where
  511. P: Properties + 'src,
  512. {
  513. let vcomp = VProps::new(component, P::memoize, props);
  514. // cast off the lifetime of the render return
  515. let as_dyn: Box<dyn AnyProps<'src> + '_> = Box::new(vcomp);
  516. let extended: Box<dyn AnyProps<'src> + 'src> = unsafe { std::mem::transmute(as_dyn) };
  517. DynamicNode::Component(VComponent {
  518. name: fn_name,
  519. render_fn: component as *const (),
  520. static_props: P::IS_STATIC,
  521. props: RefCell::new(Some(extended)),
  522. scope: Cell::new(None),
  523. })
  524. }
  525. /// Create a new [`EventHandler`] from an [`FnMut`]
  526. pub fn event_handler<T>(&'src self, f: impl FnMut(T) + 'src) -> EventHandler<'src, T> {
  527. let handler: &mut dyn FnMut(T) = self.bump().alloc(f);
  528. let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
  529. let callback = RefCell::new(Some(caller));
  530. EventHandler { callback }
  531. }
  532. /// Create a new [`AttributeValue`] with the listener variant from a callback
  533. ///
  534. /// The callback must be confined to the lifetime of the ScopeState
  535. pub fn listener<T: 'static>(
  536. &'src self,
  537. mut callback: impl FnMut(Event<T>) + 'src,
  538. ) -> AttributeValue<'src> {
  539. // safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
  540. // This is the suggested way to build a bumpbox
  541. //
  542. // In theory, we could just use regular boxes
  543. let boxed: BumpBox<'src, dyn FnMut(_) + 'src> = unsafe {
  544. BumpBox::from_raw(self.bump().alloc(move |event: Event<dyn Any>| {
  545. if let Ok(data) = event.data.downcast::<T>() {
  546. callback(Event {
  547. propagates: event.propagates,
  548. data,
  549. });
  550. }
  551. }))
  552. };
  553. AttributeValue::Listener(RefCell::new(Some(boxed)))
  554. }
  555. /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
  556. pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue<'src> {
  557. // safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
  558. // This is the suggested way to build a bumpbox
  559. //
  560. // In theory, we could just use regular boxes
  561. let boxed: BumpBox<'src, dyn AnyValue> =
  562. unsafe { BumpBox::from_raw(self.bump().alloc(value)) };
  563. AttributeValue::Any(RefCell::new(Some(boxed)))
  564. }
  565. /// Inject an error into the nearest error boundary and quit rendering
  566. ///
  567. /// The error doesn't need to implement Error or any specific traits since the boundary
  568. /// itself will downcast the error into a trait object.
  569. pub fn throw(&self, error: impl Debug + 'static) -> Option<()> {
  570. if let Some(cx) = self.consume_context::<Rc<ErrorBoundary>>() {
  571. cx.insert_error(self.scope_id(), Box::new(error));
  572. }
  573. // Always return none during a throw
  574. None
  575. }
  576. /// Mark this component as suspended and then return None
  577. pub fn suspend(&self) -> Option<Element> {
  578. self.suspended.set(true);
  579. None
  580. }
  581. /// Store a value between renders. The foundational hook for all other hooks.
  582. ///
  583. /// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
  584. ///
  585. /// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait
  586. ///
  587. /// # Example
  588. ///
  589. /// ```
  590. /// use dioxus_core::ScopeState;
  591. ///
  592. /// // prints a greeting on the initial render
  593. /// pub fn use_hello_world(cx: &ScopeState) {
  594. /// cx.use_hook(|| println!("Hello, world!"));
  595. /// }
  596. /// ```
  597. #[allow(clippy::mut_from_ref)]
  598. pub fn use_hook<State: 'static>(&self, initializer: impl FnOnce() -> State) -> &mut State {
  599. let cur_hook = self.hook_idx.get();
  600. let mut hooks = self.hooks.try_borrow_mut().expect("The hook list is already borrowed: This error is likely caused by trying to use a hook inside a hook which violates the rules of hooks.");
  601. if cur_hook >= hooks.len() {
  602. hooks.push(Box::new(UnsafeCell::new(initializer())));
  603. }
  604. hooks
  605. .get(cur_hook)
  606. .and_then(|inn| {
  607. self.hook_idx.set(cur_hook + 1);
  608. let raw_ref = unsafe { &mut *inn.get() };
  609. raw_ref.downcast_mut::<State>()
  610. })
  611. .expect(
  612. r#"
  613. Unable to retrieve the hook that was initialized at this index.
  614. Consult the `rules of hooks` to understand how to use hooks properly.
  615. You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
  616. Functions prefixed with "use" should never be called conditionally.
  617. "#,
  618. )
  619. }
  620. }