diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 570713a91..88dc25ca3 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -73,9 +73,9 @@ pub(crate) mod innerlude { } pub use crate::innerlude::{ - fc_to_builder, vdom_is_rendering, AnyValue, Attribute, AttributeValue, CapturedError, - Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, Mutation, - MutationsVec, Properties, RenderReturn, ScopeId, ScopeState, TaskId, Template, + fc_to_builder, generation, once, vdom_is_rendering, AnyValue, Attribute, AttributeValue, + CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, + Mutation, MutationsVec, Properties, RenderReturn, ScopeId, ScopeState, TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom, }; @@ -84,11 +84,11 @@ pub use crate::innerlude::{ /// This includes types like [`Scope`], [`Element`], and [`Component`]. pub mod prelude { pub use crate::innerlude::{ - consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, has_context, - provide_context, provide_context_to_scope, provide_root_context, push_future, - remove_future, schedule_update_any, spawn, spawn_forever, suspend, use_error_boundary, - AnyValue, Component, Element, ErrorBoundary, Event, EventHandler, Fragment, - IntoAttributeValue, IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, ScopeState, + consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation, + has_context, once, provide_context, provide_context_to_scope, provide_root_context, + push_future, remove_future, schedule_update_any, spawn, spawn_forever, suspend, + use_error_boundary, AnyValue, Component, Element, ErrorBoundary, Event, EventHandler, + Fragment, IntoAttributeValue, IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, TaskId, Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom, }; } diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index f6da0189e..9fc6caad8 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -269,7 +269,6 @@ impl ScopeContext { /// cx.use_hook(|| println!("Hello, world!")); /// } /// ``` - #[allow(clippy::mut_from_ref)] pub fn use_hook(&self, initializer: impl FnOnce() -> State) -> State { let cur_hook = self.hook_index.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."); @@ -398,3 +397,30 @@ pub fn spawn_forever(fut: impl Future + 'static) -> Option pub fn remove_future(id: TaskId) { with_current_scope(|cx| cx.remove_future(id)); } + +/// 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() { +/// once(|| println!("Hello, world!")); +/// } +/// ``` +pub fn once(initializer: impl FnOnce() -> State) -> State { + with_current_scope(|cx| cx.use_hook(initializer)).expect("to be in a dioxus runtime") +} + +/// Get the current render since the inception of this component +/// +/// This can be used as a helpful diagnostic when debugging hooks/renders, etc +pub fn generation() -> Option { + with_current_scope(|cx| Some(cx.generation())).expect("to be in a dioxus runtime") +}