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
bump_frame ::BumpFrame ,
2023-08-04 20:53:05 +00:00
innerlude ::ErrorBoundary ,
2022-12-01 04:54:30 +00:00
innerlude ::{ DynamicNode , EventHandler , VComponent , VText } ,
2022-11-06 08:48:34 +00:00
lazynodes ::LazyNodes ,
2023-07-14 23:15:20 +00:00
nodes ::{ IntoAttributeValue , IntoDynNode , RenderReturn } ,
2023-08-04 20:53:05 +00:00
runtime ::Runtime ,
scope_context ::ScopeContext ,
2023-02-08 23:51:28 +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 } ;
2022-11-19 23:24:28 +00:00
use std ::{
2023-08-04 20:53:05 +00:00
any ::Any ,
2023-08-04 21:12:59 +00:00
cell ::{ Cell , Ref , RefCell , UnsafeCell } ,
2022-12-20 19:13:06 +00:00
fmt ::{ Arguments , Debug } ,
2022-12-01 04:54:30 +00:00
future ::Future ,
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
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 {
2023-08-04 20:53:05 +00:00
pub ( crate ) runtime : Rc < Runtime > ,
pub ( crate ) context_id : ScopeId ,
2022-11-18 06:31:14 +00:00
pub ( crate ) render_cnt : Cell < usize > ,
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-07-06 19:43:04 +00:00
pub ( crate ) hooks : RefCell < Vec < Box < UnsafeCell < 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-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 > > > ,
2021-09-13 22:55:43 +00:00
}
2023-08-04 21:12:59 +00:00
impl Drop for ScopeState {
fn drop ( & mut self ) {
self . runtime . remove_context ( self . context_id ) ;
}
}
2022-12-01 04:54:30 +00:00
impl < ' src > ScopeState {
2023-08-04 21:12:59 +00:00
pub ( crate ) fn context ( & self ) -> Ref < '_ , ScopeContext > {
2023-08-04 20:53:05 +00:00
self . runtime . get_context ( self . context_id ) . unwrap ( )
}
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 {
2023-08-04 20:53:05 +00:00
self . context ( ) . name
2022-12-07 01:41:47 +00:00
}
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 {
2023-08-04 20:53:05 +00:00
self . context ( ) . height
2022-11-02 01:42:29 +00:00
}
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);
/// ```
2023-08-04 20:53:05 +00:00
pub fn parent_id ( & self ) -> Option < ScopeId > {
2022-11-02 01:42:29 +00:00
// safety: the pointer to our parent is *always* valid thanks to the bump arena
2023-08-04 20:53:05 +00:00
self . context ( ) . parent_id ( )
2022-11-02 01:42:29 +00:00
}
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 {
2023-08-04 20:53:05 +00:00
self . context ( ) . scope_id ( )
2022-11-02 01:42:29 +00:00
}
/// 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 > {
2023-08-04 20:53:05 +00:00
self . context ( ) . schedule_update ( )
2022-11-02 01:42:29 +00:00
}
/// 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 > {
2023-08-04 20:53:05 +00:00
self . context ( ) . schedule_update_any ( )
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 ) {
2023-08-04 20:53:05 +00:00
self . context ( ) . needs_update ( )
2022-11-02 01:42:29 +00:00
}
/// 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 ) {
2023-08-04 20:53:05 +00:00
self . context ( ) . needs_update_any ( id )
2022-11-02 01:42:29 +00:00
}
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 > {
2023-08-04 20:53:05 +00:00
self . context ( ) . has_context ( )
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 > {
2023-08-04 20:53:05 +00:00
self . context ( ) . consume_context ( )
2022-11-19 23:24:28 +00:00
}
2023-02-27 21:05:49 +00:00
/// Expose state to children further down the [`crate::VirtualDom`] Tree. Requires `Clone` on the context to allow getting values down the tree.
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
///
/// # 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 {
2023-08-04 20:53:05 +00:00
self . context ( ) . provide_context ( value )
2022-11-02 01:42:29 +00:00
}
2023-07-14 19:52:49 +00:00
/// Provide a context to the root and then consume it
///
/// This is intended for "global" state management solutions that would rather be implicit for the entire app.
/// Things like signal runtimes and routers are examples of "singletons" that would benefit from lazy initialization.
///
/// Note that you should be checking if the context existed before trying to provide a new one. Providing a context
/// when a context already exists will swap the context out for the new one, which may not be what you want.
pub fn provide_root_context < T : 'static + Clone > ( & self , context : T ) -> T {
2023-08-04 20:53:05 +00:00
self . context ( ) . provide_root_context ( context )
2023-07-14 19:52:49 +00:00
}
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-08-04 20:53:05 +00:00
self . context ( ) . push_future ( fut )
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 ) {
2023-08-04 20:53:05 +00:00
self . context ( ) . spawn ( fut ) ;
2022-11-02 01:42:29 +00:00
}
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 {
2023-08-04 20:53:05 +00:00
self . context ( ) . spawn_forever ( fut )
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 ) {
2023-08-04 20:53:05 +00:00
self . context ( ) . remove_future ( id ) ;
2022-11-02 01:42:29 +00:00
}
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 {
2023-01-29 17:53:52 +00:00
if ! comp . static_props {
let unbounded = unsafe { std ::mem ::transmute ( comp as * const VComponent ) } ;
props . push ( unbounded ) ;
}
2022-12-17 03:54:33 +00:00
}
}
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;
/// ```
2023-08-04 21:24:49 +00:00
pub fn component < P > (
2022-12-01 04:54:30 +00:00
& ' src self ,
2023-07-14 23:15:20 +00:00
component : fn ( Scope < ' src , P > ) -> Element < ' src > ,
2022-12-01 04:54:30 +00:00
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 ) ) ;
2023-08-07 21:50:36 +00:00
EventHandler {
callback ,
origin : self . context ( ) . id ,
}
2022-12-01 04:54:30 +00:00
}
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
2023-02-08 23:51:28 +00:00
pub fn listener < T : 'static > (
2022-12-03 00:24:49 +00:00
& ' src self ,
2023-02-08 23:51:28 +00:00
mut callback : impl FnMut ( Event < T > ) + ' src ,
2022-12-03 00:24:49 +00:00
) -> AttributeValue < ' src > {
2023-08-07 21:50:36 +00:00
// 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 {
propagates : event . propagates ,
data ,
} ) ;
}
} ) )
} ;
AttributeValue ::Listener ( RefCell ::new ( Some ( boxed ) ) )
2022-12-03 00:24:49 +00:00
}
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
}
2023-07-14 23:56:17 +00:00
/// Mark this component as suspended and then return None
2023-07-15 00:12:07 +00:00
pub fn suspend ( & self ) -> Option < Element > {
2023-08-04 21:12:59 +00:00
let cx = self . context ( ) ;
cx . suspend ( ) ;
None
2023-07-14 23:56:17 +00:00
}
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-07-06 19:43:04 +00:00
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. " ) ;
2022-11-02 01:42:29 +00:00
2023-07-06 19:43:04 +00:00
if cur_hook > = hooks . len ( ) {
hooks . push ( Box ::new ( UnsafeCell ::new ( initializer ( ) ) ) ) ;
2022-11-02 01:42:29 +00:00
}
2023-07-06 19:43:04 +00:00
hooks
2022-11-19 23:24:28 +00:00
. 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 ) ;
2023-07-06 19:43:04 +00:00
let raw_ref = unsafe { & mut * inn . get ( ) } ;
raw_ref . downcast_mut ::< State > ( )
2022-11-02 01:42:29 +00:00
} )
. expect (
2023-07-18 20:16:17 +00:00
r #"
2022-11-02 01:42:29 +00:00
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 .
2023-07-18 20:16:17 +00:00
" #,
2022-11-02 01:42:29 +00:00
)
2021-12-26 19:22:30 +00:00
}
}