dioxus/packages/core/src/scopes.rs

554 lines
20 KiB
Rust
Raw Normal View History

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};
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,
rc::Rc,
sync::Arc,
};
2022-11-02 01:42:29 +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
/// [`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.
//
//
/// 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 = ()> {
/// 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,
/// The component's properties.
2022-11-02 01:42:29 +00:00
pub props: &'a T,
}
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-12-01 04:54:30 +00:00
/// This struct exists to provide a common interface for all scopes without relying on generics.
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-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
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
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-11-30 22:21:10 +00:00
pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
}
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 {
0 => &self.node_arena_1,
1 => &self.node_arena_2,
_ => unreachable!(),
2021-11-12 02:34:20 +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()
}
/// 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
}
/// 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) }
}
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-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
}
/// 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
}
/// 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()
}
/// 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()
}
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
}
/// 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
}
/// 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> {
let element = rsx.call(self);
2022-12-26 16:53:25 +00:00
let mut listeners = self.attributes_to_drop.borrow_mut();
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);
}
_ => (),
}
}
let mut props = self.borrowed_props.borrow_mut();
for node in element.dynamic_nodes {
if let DynamicNode::Component(comp) = node {
if !comp.static_props {
let unbounded = unsafe { std::mem::transmute(comp as *const VComponent) };
props.push(unbounded);
}
}
}
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(|| {
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());
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
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 {
let cur_hook = self.hook_idx.get();
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
if cur_hook >= hooks.len() {
hooks.push(Box::new(UnsafeCell::new(initializer())));
2022-11-02 01:42:29 +00:00
}
hooks
.get(cur_hook)
2022-11-02 01:42:29 +00:00
.and_then(|inn| {
self.hook_idx.set(cur_hook + 1);
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
}
}