2022-11-02 01:42:29 +00:00
use crate ::{
2022-11-06 08:48:34 +00:00
any_props ::AnyProps ,
2022-12-01 04:54:30 +00:00
any_props ::VProps ,
2022-11-06 08:48:34 +00:00
arena ::ElementId ,
bump_frame ::BumpFrame ,
2022-12-01 04:54:30 +00:00
innerlude ::{ DynamicNode , EventHandler , VComponent , VText } ,
2022-12-20 19:13:06 +00:00
innerlude ::{ ErrorBoundary , Scheduler , SchedulerMsg } ,
2022-11-06 08:48:34 +00:00
lazynodes ::LazyNodes ,
2022-12-03 00:24:49 +00:00
nodes ::{ ComponentReturn , IntoAttributeValue , IntoDynNode , RenderReturn } ,
2023-01-03 03:26:12 +00:00
AnyValue , Attribute , AttributeValue , Element , Event , Properties , TaskId ,
2022-11-02 01:42:29 +00:00
} ;
2022-12-01 04:54:30 +00:00
use bumpalo ::{ boxed ::Box as BumpBox , Bump } ;
2023-01-22 23:16:34 +00:00
use bumpslab ::{ BumpSlab , Slot } ;
2022-12-10 02:56:48 +00:00
use rustc_hash ::{ FxHashMap , FxHashSet } ;
2023-01-22 23:16:34 +00:00
use slab ::{ Slab , VacantEntry } ;
2022-11-19 23:24:28 +00:00
use std ::{
any ::{ Any , TypeId } ,
cell ::{ Cell , RefCell } ,
2022-12-20 19:13:06 +00:00
fmt ::{ Arguments , Debug } ,
2022-12-01 04:54:30 +00:00
future ::Future ,
2023-01-22 23:16:34 +00:00
ops ::{ Index , IndexMut } ,
2022-11-19 23:24:28 +00:00
rc ::Rc ,
sync ::Arc ,
} ;
2022-11-02 01:42:29 +00:00
2022-11-19 23:24:28 +00:00
/// A wrapper around the [`Scoped`] object that contains a reference to the [`ScopeState`] and properties for a given
/// component.
///
2022-12-03 00:24:49 +00:00
/// The [`Scope`] is your handle to the [`crate::VirtualDom`] and the component state. Every component is given its own
2022-11-19 23:24:28 +00:00
/// [`ScopeState`] and merged with its properties to create a [`Scoped`].
///
/// The [`Scope`] handle specifically exists to provide a stable reference to these items for the lifetime of the
/// component render.
2022-11-09 03:39:37 +00:00
pub type Scope < ' a , T = ( ) > = & ' a Scoped < ' a , T > ;
2022-11-20 01:07:29 +00:00
// This ScopedType exists because we want to limit the amount of monomorphization that occurs when making inner
// state type generic over props. When the state is generic, it causes every method to be monomorphized for every
// instance of Scope<T> in the codebase.
//
//
2022-11-19 23:24:28 +00:00
/// A wrapper around a component's [`ScopeState`] and properties. The [`ScopeState`] provides the majority of methods
/// for the VirtualDom and component state.
2022-11-09 03:39:37 +00:00
pub struct Scoped < ' a , T = ( ) > {
2022-11-19 23:24:28 +00:00
/// The component's state and handle to the scheduler.
///
/// Stores things like the custom bump arena, spawn functions, hooks, and the scheduler.
2022-11-02 01:42:29 +00:00
pub scope : & ' a ScopeState ,
2022-11-19 23:24:28 +00:00
/// The component's properties.
2022-11-02 01:42:29 +00:00
pub props : & ' a T ,
}
2022-02-27 22:03:43 +00:00
2022-11-09 03:39:37 +00:00
impl < ' a , T > std ::ops ::Deref for Scoped < ' a , T > {
2022-11-02 01:42:29 +00:00
type Target = & ' a ScopeState ;
fn deref ( & self ) -> & Self ::Target {
& self . scope
}
}
/// A component's unique identifier.
///
2022-12-03 00:24:49 +00:00
/// `ScopeId` is a `usize` that acts a key for the internal slab of Scopes. This means that the key is not unqiue across
/// time. We do try and guarantee that between calls to `wait_for_work`, no ScopeIds will be recycled in order to give
/// time for any logic that relies on these IDs to properly update.
2022-11-02 01:42:29 +00:00
#[ cfg_attr(feature = " serialize " , derive(serde::Serialize, serde::Deserialize)) ]
#[ derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord) ]
pub struct ScopeId ( pub usize ) ;
2021-12-26 19:22:30 +00:00
2023-01-22 23:16:34 +00:00
/// A thin wrapper around a BumpSlab that uses ids to index into the slab.
pub ( crate ) struct ScopeSlab {
slab : BumpSlab < ScopeState > ,
// a slab of slots of stable pointers to the ScopeState in the bump slab
entries : Slab < Slot < 'static , ScopeState > > ,
}
2023-01-23 03:24:30 +00:00
impl Drop for ScopeSlab {
fn drop ( & mut self ) {
// Bump slab doesn't drop its contents, so we need to do it manually
for slot in self . entries . drain ( ) {
self . slab . remove ( slot ) ;
}
}
}
2023-01-22 23:16:34 +00:00
impl Default for ScopeSlab {
fn default ( ) -> Self {
Self {
slab : BumpSlab ::new ( ) ,
entries : Slab ::new ( ) ,
}
}
}
impl ScopeSlab {
pub ( crate ) fn get ( & self , id : ScopeId ) -> Option < & ScopeState > {
self . entries . get ( id . 0 ) . map ( | slot | unsafe { & * slot . ptr ( ) } )
}
pub ( crate ) fn get_mut ( & mut self , id : ScopeId ) -> Option < & mut ScopeState > {
self . entries
. get ( id . 0 )
. map ( | slot | unsafe { & mut * slot . ptr_mut ( ) } )
}
pub ( crate ) fn vacant_entry ( & mut self ) -> ScopeSlabEntry {
let entry = self . entries . vacant_entry ( ) ;
ScopeSlabEntry {
slab : & mut self . slab ,
entry ,
}
}
pub ( crate ) fn remove ( & mut self , id : ScopeId ) {
self . slab . remove ( self . entries . remove ( id . 0 ) ) ;
}
pub ( crate ) fn contains ( & self , id : ScopeId ) -> bool {
self . entries . contains ( id . 0 )
}
pub ( crate ) fn iter ( & self ) -> impl Iterator < Item = & ScopeState > {
self . entries . iter ( ) . map ( | ( _ , slot ) | unsafe { & * slot . ptr ( ) } )
}
}
pub ( crate ) struct ScopeSlabEntry < ' a > {
slab : & ' a mut BumpSlab < ScopeState > ,
entry : VacantEntry < ' a , Slot < 'static , ScopeState > > ,
}
impl < ' a > ScopeSlabEntry < ' a > {
pub ( crate ) fn key ( & self ) -> usize {
self . entry . key ( )
}
pub ( crate ) fn insert ( self , scope : ScopeState ) -> & ' a ScopeState {
let slot = self . slab . push ( scope ) ;
// this is safe because the slot is only ever accessed with the lifetime of the borrow of the slab
let slot = unsafe { std ::mem ::transmute ( slot ) } ;
let entry = self . entry . insert ( slot ) ;
unsafe { & * entry . ptr ( ) }
}
}
impl Index < ScopeId > for ScopeSlab {
type Output = ScopeState ;
fn index ( & self , id : ScopeId ) -> & Self ::Output {
self . get ( id ) . unwrap ( )
}
}
impl IndexMut < ScopeId > for ScopeSlab {
fn index_mut ( & mut self , id : ScopeId ) -> & mut Self ::Output {
self . get_mut ( id ) . unwrap ( )
}
}
2022-12-01 04:54:30 +00:00
/// A component's state separate from its props.
2022-11-19 23:24:28 +00:00
///
2022-12-01 04:54:30 +00:00
/// This struct exists to provide a common interface for all scopes without relying on generics.
2021-12-14 07:27:59 +00:00
pub struct ScopeState {
2022-11-18 06:31:14 +00:00
pub ( crate ) render_cnt : Cell < usize > ,
2022-12-07 01:41:47 +00:00
pub ( crate ) name : & 'static str ,
2022-10-20 16:56:09 +00:00
2022-11-09 18:58:11 +00:00
pub ( crate ) node_arena_1 : BumpFrame ,
pub ( crate ) node_arena_2 : BumpFrame ,
2021-11-08 03:36:57 +00:00
2023-01-09 21:50:33 +00:00
pub ( crate ) parent : Option < * const ScopeState > ,
2022-11-09 18:58:11 +00:00
pub ( crate ) id : ScopeId ,
2021-11-03 23:55:02 +00:00
2022-11-09 18:58:11 +00:00
pub ( crate ) height : u32 ,
2021-10-25 19:05:17 +00:00
2022-11-09 18:58:11 +00:00
pub ( crate ) hook_arena : Bump ,
2022-11-19 23:24:28 +00:00
pub ( crate ) hook_list : RefCell < Vec < * mut dyn Any > > ,
2022-11-09 18:58:11 +00:00
pub ( crate ) hook_idx : Cell < usize > ,
2021-07-09 05:42:26 +00:00
2022-12-06 20:24:35 +00:00
pub ( crate ) shared_contexts : RefCell < FxHashMap < TypeId , Box < dyn Any > > > ,
2022-11-02 01:42:29 +00:00
2022-11-09 19:02:52 +00:00
pub ( crate ) tasks : Rc < Scheduler > ,
2023-01-09 21:50:33 +00:00
pub ( crate ) spawned_tasks : RefCell < FxHashSet < TaskId > > ,
2022-11-04 05:30:26 +00:00
2022-12-17 03:54:33 +00:00
pub ( crate ) borrowed_props : RefCell < Vec < * const VComponent < 'static > > > ,
2022-12-26 16:53:25 +00:00
pub ( crate ) attributes_to_drop : RefCell < Vec < * const Attribute < 'static > > > ,
2022-12-17 03:54:33 +00:00
2022-11-30 22:21:10 +00:00
pub ( crate ) props : Option < Box < dyn AnyProps < 'static > > > ,
2022-11-09 18:58:11 +00:00
pub ( crate ) placeholder : Cell < Option < ElementId > > ,
2021-09-13 22:55:43 +00:00
}
2022-12-01 04:54:30 +00:00
impl < ' src > ScopeState {
2022-11-29 21:31:04 +00:00
pub ( crate ) fn current_frame ( & self ) -> & BumpFrame {
2022-11-18 06:31:14 +00:00
match self . render_cnt . get ( ) % 2 {
2022-10-28 04:58:47 +00:00
0 = > & self . node_arena_1 ,
1 = > & self . node_arena_2 ,
_ = > unreachable! ( ) ,
2021-11-12 02:34:20 +00:00
}
}
2022-11-19 23:24:28 +00:00
2022-11-29 21:31:04 +00:00
pub ( crate ) fn previous_frame ( & self ) -> & BumpFrame {
2022-11-18 06:31:14 +00:00
match self . render_cnt . get ( ) % 2 {
2022-11-12 02:29:27 +00:00
1 = > & self . node_arena_1 ,
0 = > & self . node_arena_2 ,
_ = > unreachable! ( ) ,
}
}
2021-12-19 04:03:59 +00:00
2022-12-07 01:41:47 +00:00
/// Get the name of this component
pub fn name ( & self ) -> & str {
self . name
}
2022-11-24 07:15:01 +00:00
/// Get the current render since the inception of this component
///
/// This can be used as a helpful diagnostic when debugging hooks/renders, etc
2022-11-23 04:16:14 +00:00
pub fn generation ( & self ) -> usize {
self . render_cnt . get ( )
}
2022-11-19 23:24:28 +00:00
/// Get a handle to the currently active bump arena for this Scope
///
/// This is a bump memory allocator. Be careful using this directly since the contents will be wiped on the next render.
/// It's easy to leak memory here since the drop implementation will not be called for any objects allocated in this arena.
///
/// If you need to allocate items that need to be dropped, use bumpalo's box.
2022-11-02 01:42:29 +00:00
pub fn bump ( & self ) -> & Bump {
2022-11-29 21:31:04 +00:00
// note that this is actually the previous frame since we use that as scratch space while the component is rendering
2023-01-11 00:39:56 +00:00
self . previous_frame ( ) . bump ( )
2022-11-02 01:42:29 +00:00
}
2022-11-19 23:24:28 +00:00
/// Get a handle to the currently active head node arena for this Scope
///
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
2022-11-23 05:32:26 +00:00
///
/// Panics if the tree has not been built yet.
2022-11-29 21:46:25 +00:00
pub fn root_node ( & self ) -> & RenderReturn {
2022-11-23 05:32:26 +00:00
self . try_root_node ( )
. expect ( " The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes. " )
}
/// Try to get a handle to the currently active head node arena for this Scope
///
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
///
/// Returns [`None`] if the tree has not been built yet.
2022-11-29 21:46:25 +00:00
pub fn try_root_node ( & self ) -> Option < & RenderReturn > {
2022-11-23 05:32:26 +00:00
let ptr = self . current_frame ( ) . node . get ( ) ;
if ptr . is_null ( ) {
return None ;
}
let r : & RenderReturn = unsafe { & * ptr } ;
2022-11-02 01:42:29 +00:00
unsafe { std ::mem ::transmute ( r ) }
}
2021-11-09 17:10:11 +00:00
2022-11-02 01:42:29 +00:00
/// Get the height of this Scope - IE the number of scopes above it.
///
/// A Scope with a height of `0` is the root scope - there are no other scopes above it.
///
/// # Example
///
/// ```rust, ignore
/// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
/// dom.rebuild();
///
/// let base = dom.base_scope();
///
/// assert_eq!(base.height(), 0);
/// ```
pub fn height ( & self ) -> u32 {
self . height
}
2022-02-04 23:05:55 +00:00
2022-12-03 00:24:49 +00:00
/// Get the Parent of this [`Scope`] within this Dioxus [`crate::VirtualDom`].
2022-11-02 01:42:29 +00:00
///
2022-12-03 00:24:49 +00:00
/// This ID is not unique across Dioxus [`crate::VirtualDom`]s or across time. IDs will be reused when components are unmounted.
2022-11-02 01:42:29 +00:00
///
/// The base component will not have a parent, and will return `None`.
///
/// # Example
///
/// ```rust, ignore
/// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
/// dom.rebuild();
///
/// let base = dom.base_scope();
///
/// assert_eq!(base.parent(), None);
/// ```
pub fn parent ( & self ) -> Option < ScopeId > {
// safety: the pointer to our parent is *always* valid thanks to the bump arena
self . parent . map ( | p | unsafe { & * p } . id )
}
2022-02-27 22:03:43 +00:00
2022-12-03 00:24:49 +00:00
/// Get the ID of this Scope within this Dioxus [`crate::VirtualDom`].
2022-11-02 01:42:29 +00:00
///
2022-12-03 00:24:49 +00:00
/// This ID is not unique across Dioxus [`crate::VirtualDom`]s or across time. IDs will be reused when components are unmounted.
2022-11-02 01:42:29 +00:00
///
/// # Example
///
/// ```rust, ignore
/// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
/// dom.rebuild();
/// let base = dom.base_scope();
///
/// assert_eq!(base.scope_id(), 0);
/// ```
pub fn scope_id ( & self ) -> ScopeId {
self . id
}
/// Create a subscription that schedules a future render for the reference component
///
2022-12-03 00:24:49 +00:00
/// ## Notice: you should prefer using [`Self::schedule_update_any`] and [`Self::scope_id`]
2022-11-02 01:42:29 +00:00
pub fn schedule_update ( & self ) -> Arc < dyn Fn ( ) + Send + Sync + 'static > {
let ( chan , id ) = ( self . tasks . sender . clone ( ) , self . scope_id ( ) ) ;
Arc ::new ( move | | drop ( chan . unbounded_send ( SchedulerMsg ::Immediate ( id ) ) ) )
}
/// Schedule an update for any component given its [`ScopeId`].
///
/// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
///
/// This method should be used when you want to schedule an update for a component
pub fn schedule_update_any ( & self ) -> Arc < dyn Fn ( ScopeId ) + Send + Sync > {
let chan = self . tasks . sender . clone ( ) ;
2022-12-13 02:31:30 +00:00
Arc ::new ( move | id | {
chan . unbounded_send ( SchedulerMsg ::Immediate ( id ) ) . unwrap ( ) ;
} )
2022-11-02 01:42:29 +00:00
}
2022-11-19 23:24:28 +00:00
/// Mark this scope as dirty, and schedule a render for it.
2022-11-02 01:42:29 +00:00
pub fn needs_update ( & self ) {
self . needs_update_any ( self . scope_id ( ) ) ;
}
/// Get the [`ScopeId`] of a mounted component.
///
2022-12-03 00:24:49 +00:00
/// `ScopeId` is not unique for the lifetime of the [`crate::VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted.
2022-11-02 01:42:29 +00:00
pub fn needs_update_any ( & self , id : ScopeId ) {
self . tasks
. sender
. unbounded_send ( SchedulerMsg ::Immediate ( id ) )
. expect ( " Scheduler to exist if scope exists " ) ;
}
2022-11-19 23:24:28 +00:00
/// Return any context of type T if it exists on this scope
2022-12-05 23:30:49 +00:00
pub fn has_context < T : 'static + Clone > ( & self ) -> Option < T > {
2022-12-05 22:16:54 +00:00
self . shared_contexts
. borrow ( )
. get ( & TypeId ::of ::< T > ( ) ) ?
2022-12-05 23:30:49 +00:00
. downcast_ref ::< T > ( )
. cloned ( )
2022-11-19 23:24:28 +00:00
}
/// Try to retrieve a shared state with type `T` from any parent scope.
///
2022-12-05 23:30:49 +00:00
/// Clones the state if it exists.
pub fn consume_context < T : 'static + Clone > ( & self ) -> Option < T > {
2022-11-19 23:24:28 +00:00
if let Some ( this_ctx ) = self . has_context ( ) {
return Some ( this_ctx ) ;
}
let mut search_parent = self . parent ;
while let Some ( parent_ptr ) = search_parent {
// safety: all parent pointers are valid thanks to the bump arena
let parent = unsafe { & * parent_ptr } ;
if let Some ( shared ) = parent . shared_contexts . borrow ( ) . get ( & TypeId ::of ::< T > ( ) ) {
2022-12-05 23:30:49 +00:00
return shared . downcast_ref ::< T > ( ) . cloned ( ) ;
2022-11-19 23:24:28 +00:00
}
search_parent = parent . parent ;
}
None
}
2022-12-03 00:24:49 +00:00
/// Expose state to children further down the [`crate::VirtualDom`] Tree. Does not require `clone` on the context,
/// though we do recommend it.
2022-11-02 01:42:29 +00:00
///
/// This is a "fundamental" operation and should only be called during initialization of a hook.
///
2022-12-05 22:16:54 +00:00
/// For a hook that provides the same functionality, use `use_provide_context` and `use_context` instead.
2022-11-02 01:42:29 +00:00
///
2022-12-03 00:24:49 +00:00
/// If a state is provided that already exists, the new value will not be inserted. Instead, this method will
/// return the existing value. This behavior is chosen so shared values do not need to be `Clone`. This particular
/// behavior might change in the future.
2022-11-02 01:42:29 +00:00
///
/// # Example
///
/// ```rust, ignore
/// struct SharedState(&'static str);
///
/// static App: Component = |cx| {
/// cx.use_hook(|| cx.provide_context(SharedState("world")));
/// render!(Child {})
/// }
///
/// static Child: Component = |cx| {
/// let state = cx.consume_state::<SharedState>();
/// render!(div { "hello {state.0}" })
/// }
/// ```
2022-12-05 23:30:49 +00:00
pub fn provide_context < T : 'static + Clone > ( & self , value : T ) -> T {
let value2 = value . clone ( ) ;
self . shared_contexts
. borrow_mut ( )
. insert ( TypeId ::of ::< T > ( ) , Box ::new ( value ) ) ;
2022-12-03 00:24:49 +00:00
2022-12-05 23:30:49 +00:00
value2
2022-11-02 01:42:29 +00:00
}
/// Pushes the future onto the poll queue to be polled after the component renders.
pub fn push_future ( & self , fut : impl Future < Output = ( ) > + 'static ) -> TaskId {
2023-01-15 19:52:06 +00:00
let id = self . tasks . spawn ( self . id , fut ) ;
self . spawned_tasks . borrow_mut ( ) . insert ( id ) ;
id
2022-11-02 01:42:29 +00:00
}
/// Spawns the future but does not return the [`TaskId`]
pub fn spawn ( & self , fut : impl Future < Output = ( ) > + 'static ) {
self . push_future ( fut ) ;
}
2022-11-19 23:24:28 +00:00
/// Spawn a future that Dioxus won't clean up when this component is unmounted
2022-11-02 01:42:29 +00:00
///
/// This is good for tasks that need to be run after the component has been dropped.
pub fn spawn_forever ( & self , fut : impl Future < Output = ( ) > + 'static ) -> TaskId {
2022-11-06 08:48:34 +00:00
// The root scope will never be unmounted so we can just add the task at the top of the app
let id = self . tasks . spawn ( ScopeId ( 0 ) , fut ) ;
2022-11-02 01:42:29 +00:00
// wake up the scheduler if it is sleeping
self . tasks
. sender
2022-11-06 08:48:34 +00:00
. unbounded_send ( SchedulerMsg ::TaskNotified ( id ) )
2022-11-02 01:42:29 +00:00
. expect ( " Scheduler should exist " ) ;
2023-01-15 19:52:06 +00:00
self . spawned_tasks . borrow_mut ( ) . insert ( id ) ;
2022-11-06 08:48:34 +00:00
id
2022-11-02 01:42:29 +00:00
}
2022-11-19 23:24:28 +00:00
/// Informs the scheduler that this task is no longer needed and should be removed.
///
/// This drops the task immediately.
2022-11-02 01:42:29 +00:00
pub fn remove_future ( & self , id : TaskId ) {
self . tasks . remove ( id ) ;
}
2022-12-03 00:24:49 +00:00
/// Take a lazy [`crate::VNode`] structure and actually build it with the context of the efficient [`bumpalo::Bump`] allocator.
2022-11-02 01:42:29 +00:00
///
/// ## Example
///
/// ```ignore
/// fn Component(cx: Scope<Props>) -> Element {
/// // Lazy assemble the VNode tree
/// let lazy_nodes = rsx!("hello world");
///
/// // Actually build the tree and allocate it
/// cx.render(lazy_tree)
/// }
///```
2022-12-01 04:54:30 +00:00
pub fn render ( & ' src self , rsx : LazyNodes < ' src , '_ > ) -> Element < ' src > {
2022-12-17 03:54:33 +00:00
let element = rsx . call ( self ) ;
2022-12-26 16:53:25 +00:00
let mut listeners = self . attributes_to_drop . borrow_mut ( ) ;
2022-12-17 03:54:33 +00:00
for attr in element . dynamic_attrs {
2022-12-26 16:53:25 +00:00
match attr . value {
AttributeValue ::Any ( _ ) | AttributeValue ::Listener ( _ ) = > {
let unbounded = unsafe { std ::mem ::transmute ( attr as * const Attribute ) } ;
listeners . push ( unbounded ) ;
}
_ = > ( ) ,
2022-12-17 03:54:33 +00:00
}
}
let mut props = self . borrowed_props . borrow_mut ( ) ;
for node in element . dynamic_nodes {
if let DynamicNode ::Component ( comp ) = node {
let unbounded = unsafe { std ::mem ::transmute ( comp as * const VComponent ) } ;
props . push ( unbounded ) ;
}
}
2022-12-20 02:06:13 +00:00
Some ( element )
2022-11-02 01:42:29 +00:00
}
2022-12-01 04:54:30 +00:00
/// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
pub fn text_node ( & ' src self , args : Arguments ) -> DynamicNode < ' src > {
DynamicNode ::Text ( VText {
value : self . raw_text ( args ) ,
id : Default ::default ( ) ,
} )
}
/// Allocate some text inside the [`ScopeState`] from [`Arguments`]
///
/// Uses the currently active [`Bump`] allocator
pub fn raw_text ( & ' src self , args : Arguments ) -> & ' src str {
args . as_str ( ) . unwrap_or_else ( | | {
2022-12-07 16:11:27 +00:00
use bumpalo ::core_alloc ::fmt ::Write ;
2022-12-01 04:54:30 +00:00
let mut str_buf = bumpalo ::collections ::String ::new_in ( self . bump ( ) ) ;
2022-12-07 16:11:27 +00:00
str_buf . write_fmt ( args ) . unwrap ( ) ;
2022-12-01 04:54:30 +00:00
str_buf . into_bump_str ( )
} )
}
/// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`] using the internal [`Bump`] allocator
pub fn make_node < ' c , I > ( & ' src self , into : impl IntoDynNode < ' src , I > + ' c ) -> DynamicNode {
into . into_vnode ( self )
}
/// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
///
/// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
/// some renderers stay in sync with the VirtualDom's understanding of the world
pub fn attr (
& ' src self ,
name : & 'static str ,
value : impl IntoAttributeValue < ' src > ,
namespace : Option < & 'static str > ,
volatile : bool ,
) -> Attribute < ' src > {
Attribute {
name ,
namespace ,
volatile ,
mounted_element : Default ::default ( ) ,
value : value . into_value ( self . bump ( ) ) ,
}
}
/// Create a new [`DynamicNode::Component`] variant
///
///
/// The given component can be any of four signatures. Remember that an [`Element`] is really a [`Result<VNode>`].
///
/// ```rust, ignore
/// // Without explicit props
/// fn(Scope) -> Element;
/// async fn(Scope<'_>) -> Element;
///
/// // With explicit props
/// fn(Scope<Props>) -> Element;
/// async fn(Scope<Props<'_>>) -> Element;
/// ```
pub fn component < P , A , F : ComponentReturn < ' src , A > > (
& ' src self ,
component : fn ( Scope < ' src , P > ) -> F ,
props : P ,
fn_name : & 'static str ,
) -> DynamicNode < ' src >
where
P : Properties + ' src ,
{
let vcomp = VProps ::new ( component , P ::memoize , props ) ;
// cast off the lifetime of the render return
let as_dyn : Box < dyn AnyProps < ' src > + '_ > = Box ::new ( vcomp ) ;
let extended : Box < dyn AnyProps < ' src > + ' src > = unsafe { std ::mem ::transmute ( as_dyn ) } ;
DynamicNode ::Component ( VComponent {
name : fn_name ,
render_fn : component as * const ( ) ,
static_props : P ::IS_STATIC ,
2022-12-13 02:31:30 +00:00
props : RefCell ::new ( Some ( extended ) ) ,
2022-12-01 04:54:30 +00:00
scope : Cell ::new ( None ) ,
} )
}
/// Create a new [`EventHandler`] from an [`FnMut`]
pub fn event_handler < T > ( & ' src self , f : impl FnMut ( T ) + ' src ) -> EventHandler < ' src , T > {
let handler : & mut dyn FnMut ( T ) = self . bump ( ) . alloc ( f ) ;
let caller = unsafe { BumpBox ::from_raw ( handler as * mut dyn FnMut ( T ) ) } ;
let callback = RefCell ::new ( Some ( caller ) ) ;
EventHandler { callback }
}
2022-12-03 00:24:49 +00:00
/// Create a new [`AttributeValue`] with the listener variant from a callback
///
/// The callback must be confined to the lifetime of the ScopeState
pub fn listener < T : 'static > (
& ' src self ,
mut callback : impl FnMut ( Event < T > ) + ' src ,
) -> AttributeValue < ' src > {
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
// This is the suggested way to build a bumpbox
//
// In theory, we could just use regular boxes
let boxed : BumpBox < ' src , dyn FnMut ( _ ) + ' src > = unsafe {
BumpBox ::from_raw ( self . bump ( ) . alloc ( move | event : Event < dyn Any > | {
if let Ok ( data ) = event . data . downcast ::< T > ( ) {
callback ( Event {
2023-01-01 20:41:18 +00:00
propagates : event . propagates ,
2022-12-03 00:24:49 +00:00
data ,
} )
}
} ) )
} ;
AttributeValue ::Listener ( RefCell ::new ( Some ( boxed ) ) )
}
2023-01-03 03:26:12 +00:00
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
pub fn any_value < T : AnyValue > ( & ' src self , value : T ) -> AttributeValue < ' src > {
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
// This is the suggested way to build a bumpbox
//
// In theory, we could just use regular boxes
let boxed : BumpBox < ' src , dyn AnyValue > =
unsafe { BumpBox ::from_raw ( self . bump ( ) . alloc ( value ) ) } ;
AttributeValue ::Any ( RefCell ::new ( Some ( boxed ) ) )
}
2022-12-20 19:13:06 +00:00
/// Inject an error into the nearest error boundary and quit rendering
///
/// The error doesn't need to implement Error or any specific traits since the boundary
/// itself will downcast the error into a trait object.
pub fn throw ( & self , error : impl Debug + 'static ) -> Option < ( ) > {
if let Some ( cx ) = self . consume_context ::< Rc < ErrorBoundary > > ( ) {
cx . insert_error ( self . scope_id ( ) , Box ::new ( error ) ) ;
}
// Always return none during a throw
None
}
2022-11-02 01:42:29 +00:00
/// Store a value between renders. The foundational hook for all other hooks.
///
/// 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`.
///
/// 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
///
/// # Example
///
/// ```
/// use dioxus_core::ScopeState;
///
/// // prints a greeting on the initial render
/// pub fn use_hello_world(cx: &ScopeState) {
/// cx.use_hook(|| println!("Hello, world!"));
/// }
/// ```
#[ allow(clippy::mut_from_ref) ]
pub fn use_hook < State : 'static > ( & self , initializer : impl FnOnce ( ) -> State ) -> & mut State {
2022-11-19 23:24:28 +00:00
let cur_hook = self . hook_idx . get ( ) ;
2023-01-05 19:05:04 +00:00
let mut hook_list = self . hook_list . 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. " ) ;
2022-11-02 01:42:29 +00:00
2022-11-19 23:24:28 +00:00
if cur_hook > = hook_list . len ( ) {
hook_list . push ( self . hook_arena . alloc ( initializer ( ) ) ) ;
2022-11-02 01:42:29 +00:00
}
2022-11-19 23:24:28 +00:00
hook_list
. get ( cur_hook )
2022-11-02 01:42:29 +00:00
. and_then ( | inn | {
2022-11-19 23:24:28 +00:00
self . hook_idx . set ( cur_hook + 1 ) ;
2022-11-02 01:42:29 +00:00
let raw_box = unsafe { & mut * * inn } ;
raw_box . downcast_mut ::< State > ( )
} )
. expect (
r ###"
Unable to retrieve the hook that was initialized at this index .
Consult the ` rules of hooks ` to understand how to use hooks properly .
You likely used the hook in a conditional . Hooks rely on consistent ordering between renders .
Functions prefixed with " use " should never be called conditionally .
" ###,
)
2021-12-26 19:22:30 +00:00
}
}