mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 12:43:08 +00:00
move diffing into the global runtime
This commit is contained in:
parent
f42ef3ef9d
commit
c70e2bfcb6
8 changed files with 152 additions and 210 deletions
|
@ -1,14 +1,40 @@
|
||||||
use crate::{nodes::RenderReturn, scopes::ScopeState, Element};
|
use crate::{nodes::RenderReturn, Element};
|
||||||
use std::panic::AssertUnwindSafe;
|
use std::{ops::Deref, panic::AssertUnwindSafe};
|
||||||
|
|
||||||
|
/// A boxed version of AnyProps that can be cloned
|
||||||
|
pub(crate) struct BoxedAnyProps {
|
||||||
|
inner: Box<dyn AnyProps>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoxedAnyProps {
|
||||||
|
fn new(inner: impl AnyProps + 'static) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::new(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for BoxedAnyProps {
|
||||||
|
type Target = dyn AnyProps;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&*self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for BoxedAnyProps {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.duplicate(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A trait that essentially allows VComponentProps to be used generically
|
/// A trait that essentially allows VComponentProps to be used generically
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This should not be implemented outside this module
|
|
||||||
pub(crate) trait AnyProps {
|
pub(crate) trait AnyProps {
|
||||||
fn render<'a>(&'a self, bump: &'a ScopeState) -> RenderReturn;
|
fn render<'a>(&'a self) -> RenderReturn;
|
||||||
fn memoize(&self, other: &dyn AnyProps) -> bool;
|
fn memoize(&self, other: &dyn AnyProps) -> bool;
|
||||||
|
fn duplicate(&self) -> Box<dyn AnyProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct VProps<P> {
|
pub(crate) struct VProps<P> {
|
||||||
|
@ -36,7 +62,7 @@ impl<P: Clone> AnyProps for VProps<P> {
|
||||||
(self.memo)(self, other)
|
(self.memo)(self, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, cx: &ScopeState) -> RenderReturn {
|
fn render(&self) -> RenderReturn {
|
||||||
let res = std::panic::catch_unwind(AssertUnwindSafe(move || {
|
let res = std::panic::catch_unwind(AssertUnwindSafe(move || {
|
||||||
// Call the render function directly
|
// Call the render function directly
|
||||||
(self.render_fn)(self.props.clone())
|
(self.render_fn)(self.props.clone())
|
||||||
|
@ -52,4 +78,12 @@ impl<P: Clone> AnyProps for VProps<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn duplicate(&self) -> Box<dyn AnyProps> {
|
||||||
|
Box::new(Self {
|
||||||
|
render_fn: self.render_fn,
|
||||||
|
memo: self.memo,
|
||||||
|
props: self.props.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
innerlude::DirtyScope, nodes::RenderReturn, nodes::VNode, virtual_dom::VirtualDom,
|
innerlude::DirtyScope, nodes::RenderReturn, nodes::VNode, virtual_dom::VirtualDom, DynamicNode,
|
||||||
AttributeValue, DynamicNode, ScopeId,
|
ScopeId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An Element's unique identifier.
|
/// An Element's unique identifier.
|
||||||
|
@ -48,14 +48,6 @@ impl VirtualDom {
|
||||||
std::mem::transmute::<NonNull<VNode>, _>(vnode.into())
|
std::mem::transmute::<NonNull<VNode>, _>(vnode.into())
|
||||||
})));
|
})));
|
||||||
|
|
||||||
// Set this id to be dropped when the scope is rerun
|
|
||||||
if let Some(scope) = self.runtime.current_scope_id() {
|
|
||||||
self.scopes[scope.0]
|
|
||||||
.element_refs_to_drop
|
|
||||||
.borrow_mut()
|
|
||||||
.push(new_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
new_id
|
new_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,17 +81,6 @@ impl VirtualDom {
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove all VNode ids from the scope
|
|
||||||
for id in self.scopes[id.0]
|
|
||||||
.element_refs_to_drop
|
|
||||||
.borrow_mut()
|
|
||||||
.drain(..)
|
|
||||||
{
|
|
||||||
self.element_refs.try_remove(id.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ensure_drop_safety(id);
|
|
||||||
|
|
||||||
if recursive {
|
if recursive {
|
||||||
if let Some(root) = self.scopes[id.0].try_root_node() {
|
if let Some(root) = self.scopes[id.0].try_root_node() {
|
||||||
if let RenderReturn::Ready(node) = root {
|
if let RenderReturn::Ready(node) = root {
|
||||||
|
@ -110,18 +91,6 @@ impl VirtualDom {
|
||||||
|
|
||||||
let scope = &mut self.scopes[id.0];
|
let scope = &mut self.scopes[id.0];
|
||||||
|
|
||||||
// Drop all the hooks once the children are dropped
|
|
||||||
// this means we'll drop hooks bottom-up
|
|
||||||
scope.hooks.get_mut().clear();
|
|
||||||
{
|
|
||||||
let context = scope.context();
|
|
||||||
|
|
||||||
// Drop all the futures once the hooks are dropped
|
|
||||||
for task_id in context.spawned_tasks.borrow_mut().drain() {
|
|
||||||
context.tasks.remove(task_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.scopes.remove(id.0);
|
self.scopes.remove(id.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,45 +108,6 @@ impl VirtualDom {
|
||||||
DynamicNode::Text(_) => {}
|
DynamicNode::Text(_) => {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Descend through the tree, removing any borrowed props and listeners
|
|
||||||
pub(crate) fn ensure_drop_safety(&mut self, scope_id: ScopeId) {
|
|
||||||
let scope = &self.scopes[scope_id.0];
|
|
||||||
|
|
||||||
{
|
|
||||||
// Drop all element refs that could be invalidated when the component was rerun
|
|
||||||
let mut element_refs = self.scopes[scope_id.0].element_refs_to_drop.borrow_mut();
|
|
||||||
let element_refs_slab = &mut self.element_refs;
|
|
||||||
for element_ref in element_refs.drain(..) {
|
|
||||||
if let Some(element_ref) = element_refs_slab.get_mut(element_ref.0) {
|
|
||||||
*element_ref = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
|
|
||||||
// run the hooks (which hold an &mut Reference)
|
|
||||||
// recursively call ensure_drop_safety on all children
|
|
||||||
let props = { scope.borrowed_props.borrow_mut().clone() };
|
|
||||||
for comp in props {
|
|
||||||
let comp = unsafe { &*comp };
|
|
||||||
match comp.scope.get() {
|
|
||||||
Some(child) if child != scope_id => self.ensure_drop_safety(child),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let scope = &self.scopes[scope_id.0];
|
|
||||||
scope.borrowed_props.borrow_mut().clear();
|
|
||||||
|
|
||||||
// Now that all the references are gone, we can safely drop our own references in our listeners.
|
|
||||||
let mut listeners = scope.attributes_to_drop_before_render.borrow_mut();
|
|
||||||
listeners.drain(..).for_each(|listener| {
|
|
||||||
let listener = unsafe { &*listener };
|
|
||||||
if let AttributeValue::Listener(l) = &listener.value {
|
|
||||||
_ = l.take();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementPath {
|
impl ElementPath {
|
||||||
|
|
|
@ -219,13 +219,12 @@ impl VirtualDom {
|
||||||
// copy out the box for both
|
// copy out the box for both
|
||||||
let old_scope = &self.scopes[scope_id.0];
|
let old_scope = &self.scopes[scope_id.0];
|
||||||
let old = old_scope.props.as_ref();
|
let old = old_scope.props.as_ref();
|
||||||
let new: Box<dyn AnyProps> = right.props.take().unwrap();
|
let new: &dyn AnyProps = right.props.as_ref();
|
||||||
let new: Box<dyn AnyProps> = unsafe { std::mem::transmute(new) };
|
|
||||||
|
|
||||||
// If the props are static, then we try to memoize by setting the new with the old
|
// If the props are static, then we try to memoize by setting the new with the old
|
||||||
// The target scopestate still has the reference to the old props, so there's no need to update anything
|
// The target scopestate still has the reference to the old props, so there's no need to update anything
|
||||||
// This also implicitly drops the new props since they're not used
|
// This also implicitly drops the new props since they're not used
|
||||||
if left.static_props && unsafe { old.as_ref().unwrap().memoize(new.as_ref()) } {
|
if old.memoize(new) {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
"Memoized props for component {:#?} ({})",
|
"Memoized props for component {:#?} ({})",
|
||||||
scope_id,
|
scope_id,
|
||||||
|
@ -235,7 +234,7 @@ impl VirtualDom {
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, move over the props from the old to the new, dropping old props in the process
|
// First, move over the props from the old to the new, dropping old props in the process
|
||||||
self.scopes[scope_id.0].props = Some(new);
|
self.scopes[scope_id.0].props = new;
|
||||||
|
|
||||||
// Now run the component and diff it
|
// Now run the component and diff it
|
||||||
self.run_scope(scope_id);
|
self.run_scope(scope_id);
|
||||||
|
|
|
@ -341,7 +341,7 @@ pub struct VComponent {
|
||||||
/// It is possible that components get folded at compile time, so these shouldn't be really used as a key
|
/// It is possible that components get folded at compile time, so these shouldn't be really used as a key
|
||||||
pub(crate) render_fn: *const (),
|
pub(crate) render_fn: *const (),
|
||||||
|
|
||||||
pub(crate) props: Box<dyn AnyProps>,
|
pub(crate) props: BoxedAnyProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VComponent {
|
impl<'a> VComponent {
|
||||||
|
@ -362,7 +362,7 @@ impl<'a> std::fmt::Debug for VComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An instance of some text, mounted to the DOM
|
/// An instance of some text, mounted to the DOM
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct VText {
|
pub struct VText {
|
||||||
/// The actual text itself
|
/// The actual text itself
|
||||||
pub value: String,
|
pub value: String,
|
||||||
|
@ -436,7 +436,7 @@ pub enum TemplateAttribute {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
|
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Attribute {
|
pub struct Attribute {
|
||||||
/// The name of the attribute.
|
/// The name of the attribute.
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
|
@ -483,6 +483,7 @@ impl Attribute {
|
||||||
///
|
///
|
||||||
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
|
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
|
||||||
/// variant.
|
/// variant.
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum AttributeValue {
|
pub enum AttributeValue {
|
||||||
/// Text attribute
|
/// Text attribute
|
||||||
Text(String),
|
Text(String),
|
||||||
|
@ -497,10 +498,10 @@ pub enum AttributeValue {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
|
||||||
/// A listener, like "onclick"
|
/// A listener, like "onclick"
|
||||||
Listener(RefCell<Option<ListenerCb>>),
|
Listener(ListenerCb),
|
||||||
|
|
||||||
/// An arbitrary value that implements PartialEq and is static
|
/// An arbitrary value that implements PartialEq and is static
|
||||||
Any(RefCell<Option<Box<dyn AnyValue>>>),
|
Any(Box<dyn AnyValue>),
|
||||||
|
|
||||||
/// A "none" value, resulting in the removal of an attribute from the dom
|
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -24,15 +24,8 @@ impl VirtualDom {
|
||||||
runtime: self.runtime.clone(),
|
runtime: self.runtime.clone(),
|
||||||
context_id: id,
|
context_id: id,
|
||||||
|
|
||||||
props: Some(props),
|
props,
|
||||||
|
last_rendered_node: Default::default(),
|
||||||
render_cnt: Default::default(),
|
|
||||||
hooks: Default::default(),
|
|
||||||
hook_idx: Default::default(),
|
|
||||||
|
|
||||||
borrowed_props: Default::default(),
|
|
||||||
attributes_to_drop_before_render: Default::default(),
|
|
||||||
element_refs_to_drop: Default::default(),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let context =
|
let context =
|
||||||
|
@ -42,42 +35,30 @@ impl VirtualDom {
|
||||||
scope
|
scope
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {
|
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> RenderReturn {
|
||||||
self.runtime.scope_stack.borrow_mut().push(scope_id);
|
self.runtime.scope_stack.borrow_mut().push(scope_id);
|
||||||
// Cycle to the next frame and then reset it
|
|
||||||
// This breaks any latent references, invalidating every pointer referencing into it.
|
|
||||||
// Remove all the outdated listeners
|
|
||||||
self.ensure_drop_safety(scope_id);
|
|
||||||
|
|
||||||
let new_nodes = unsafe {
|
let new_nodes = unsafe {
|
||||||
let scope = &self.scopes[scope_id.0];
|
let scope = &self.scopes[scope_id.0];
|
||||||
scope.previous_frame().reset();
|
|
||||||
|
|
||||||
scope.context().suspended.set(false);
|
let context = scope.context();
|
||||||
|
context.suspended.set(false);
|
||||||
scope.hook_idx.set(0);
|
context.hook_index.set(0);
|
||||||
|
|
||||||
// safety: due to how we traverse the tree, we know that the scope is not currently aliased
|
// safety: due to how we traverse the tree, we know that the scope is not currently aliased
|
||||||
let props: &dyn AnyProps = scope.props.as_ref().unwrap().as_ref();
|
let props: &dyn AnyProps = &*scope.props;
|
||||||
let props: &dyn AnyProps = std::mem::transmute(props);
|
|
||||||
|
|
||||||
let _span = tracing::trace_span!("render", scope = %scope.context().name);
|
let _span = tracing::trace_span!("render", scope = %scope.context().name);
|
||||||
props.render(scope)
|
props.render()
|
||||||
};
|
};
|
||||||
|
|
||||||
let scope = &self.scopes[scope_id.0];
|
let scope = &mut self.scopes[scope_id.0];
|
||||||
|
|
||||||
// We write on top of the previous frame and then make it the current by pushing the generation forward
|
|
||||||
let frame = scope.previous_frame();
|
|
||||||
|
|
||||||
// set the new head of the bump frame
|
|
||||||
let allocated = &*frame.bump().alloc(new_nodes);
|
|
||||||
frame.node.set(allocated);
|
|
||||||
|
|
||||||
// And move the render generation forward by one
|
|
||||||
scope.render_cnt.set(scope.render_cnt.get() + 1);
|
|
||||||
|
|
||||||
let context = scope.context();
|
let context = scope.context();
|
||||||
|
|
||||||
|
// And move the render generation forward by one
|
||||||
|
context.render_count.set(context.render_count.get() + 1);
|
||||||
|
|
||||||
// remove this scope from dirty scopes
|
// remove this scope from dirty scopes
|
||||||
self.dirty_scopes.remove(&DirtyScope {
|
self.dirty_scopes.remove(&DirtyScope {
|
||||||
height: context.height,
|
height: context.height,
|
||||||
|
@ -85,18 +66,15 @@ impl VirtualDom {
|
||||||
});
|
});
|
||||||
|
|
||||||
if context.suspended.get() {
|
if context.suspended.get() {
|
||||||
if matches!(allocated, RenderReturn::Aborted(_)) {
|
if matches!(new_nodes, RenderReturn::Aborted(_)) {
|
||||||
self.suspended_scopes.insert(context.id);
|
self.suspended_scopes.insert(context.id);
|
||||||
}
|
}
|
||||||
} else if !self.suspended_scopes.is_empty() {
|
} else if !self.suspended_scopes.is_empty() {
|
||||||
_ = self.suspended_scopes.remove(&context.id);
|
_ = self.suspended_scopes.remove(&context.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// rebind the lifetime now that its stored internally
|
|
||||||
let result = unsafe { allocated };
|
|
||||||
|
|
||||||
self.runtime.scope_stack.borrow_mut().pop();
|
self.runtime.scope_stack.borrow_mut().pop();
|
||||||
|
|
||||||
result
|
new_nodes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,15 @@ pub(crate) struct ScopeContext {
|
||||||
pub(crate) parent_id: Option<ScopeId>,
|
pub(crate) parent_id: Option<ScopeId>,
|
||||||
|
|
||||||
pub(crate) height: u32,
|
pub(crate) height: u32,
|
||||||
|
pub(crate) render_count: Cell<usize>,
|
||||||
|
|
||||||
pub(crate) suspended: Cell<bool>,
|
pub(crate) suspended: Cell<bool>,
|
||||||
|
|
||||||
pub(crate) shared_contexts: RefCell<Vec<Box<dyn Any>>>,
|
pub(crate) shared_contexts: RefCell<Vec<Box<dyn Any>>>,
|
||||||
|
|
||||||
|
pub(crate) hooks: RefCell<Vec<Box<dyn Any>>>,
|
||||||
|
pub(crate) hook_index: Cell<usize>,
|
||||||
|
|
||||||
pub(crate) tasks: Rc<Scheduler>,
|
pub(crate) tasks: Rc<Scheduler>,
|
||||||
pub(crate) spawned_tasks: RefCell<FxHashSet<TaskId>>,
|
pub(crate) spawned_tasks: RefCell<FxHashSet<TaskId>>,
|
||||||
}
|
}
|
||||||
|
@ -43,10 +48,13 @@ impl ScopeContext {
|
||||||
id,
|
id,
|
||||||
parent_id,
|
parent_id,
|
||||||
height,
|
height,
|
||||||
|
render_count: Cell::new(0),
|
||||||
suspended: Cell::new(false),
|
suspended: Cell::new(false),
|
||||||
shared_contexts: RefCell::new(vec![]),
|
shared_contexts: RefCell::new(vec![]),
|
||||||
tasks,
|
tasks,
|
||||||
spawned_tasks: RefCell::new(FxHashSet::default()),
|
spawned_tasks: RefCell::new(FxHashSet::default()),
|
||||||
|
hooks: RefCell::new(vec![]),
|
||||||
|
hook_index: Cell::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +252,65 @@ impl ScopeContext {
|
||||||
self.suspended.set(true);
|
self.suspended.set(true);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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_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.");
|
||||||
|
|
||||||
|
if cur_hook >= hooks.len() {
|
||||||
|
hooks.push(Box::new(initializer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks
|
||||||
|
.get(cur_hook)
|
||||||
|
.and_then(|inn| {
|
||||||
|
self.hook_index.set(cur_hook + 1);
|
||||||
|
let raw_ref: &mut dyn Any = inn.as_mut();
|
||||||
|
raw_ref.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.
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(&self) -> usize {
|
||||||
|
self.render_count.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ScopeContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Drop all spawned tasks
|
||||||
|
for id in self.spawned_tasks.borrow().iter() {
|
||||||
|
self.tasks.remove(*id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule an update for any component given its [`ScopeId`].
|
/// Schedule an update for any component given its [`ScopeId`].
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
any_props::AnyProps,
|
any_props::AnyProps,
|
||||||
any_props::VProps,
|
any_props::VProps,
|
||||||
innerlude::{DynamicNode, EventHandler, VComponent, VNodeId, VText},
|
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||||
nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
|
nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||||
runtime::Runtime,
|
runtime::Runtime,
|
||||||
scope_context::ScopeContext,
|
scope_context::ScopeContext,
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::{Cell, Ref, RefCell, UnsafeCell},
|
cell::{Ref, RefCell},
|
||||||
fmt::{Arguments, Debug},
|
fmt::{Arguments, Debug},
|
||||||
future::Future,
|
future::Future,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
@ -47,16 +47,9 @@ pub struct ScopeState {
|
||||||
pub(crate) runtime: Rc<Runtime>,
|
pub(crate) runtime: Rc<Runtime>,
|
||||||
pub(crate) context_id: ScopeId,
|
pub(crate) context_id: ScopeId,
|
||||||
|
|
||||||
pub(crate) render_cnt: Cell<usize>,
|
pub(crate) last_rendered_node: Option<RenderReturn>,
|
||||||
|
|
||||||
pub(crate) hooks: RefCell<Vec<Box<UnsafeCell<dyn Any>>>>,
|
pub(crate) props: Box<dyn AnyProps>,
|
||||||
pub(crate) hook_idx: Cell<usize>,
|
|
||||||
|
|
||||||
pub(crate) borrowed_props: RefCell<Vec<*const VComponent>>,
|
|
||||||
pub(crate) element_refs_to_drop: RefCell<Vec<VNodeId>>,
|
|
||||||
pub(crate) attributes_to_drop_before_render: RefCell<Vec<*const Attribute>>,
|
|
||||||
|
|
||||||
pub(crate) props: Option<Box<dyn AnyProps>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ScopeState {
|
impl Drop for ScopeState {
|
||||||
|
@ -75,13 +68,6 @@ impl<'src> ScopeState {
|
||||||
self.context().name
|
self.context().name
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(&self) -> usize {
|
|
||||||
self.render_cnt.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a handle to the currently active head node arena for this Scope
|
/// 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.
|
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||||
|
@ -98,15 +84,7 @@ impl<'src> ScopeState {
|
||||||
///
|
///
|
||||||
/// Returns [`None`] if the tree has not been built yet.
|
/// Returns [`None`] if the tree has not been built yet.
|
||||||
pub fn try_root_node(&self) -> Option<&RenderReturn> {
|
pub fn try_root_node(&self) -> Option<&RenderReturn> {
|
||||||
let ptr = self.current_frame().node.get();
|
self.last_rendered_node.as_ref()
|
||||||
|
|
||||||
if ptr.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let r: &RenderReturn = unsafe { &*ptr };
|
|
||||||
|
|
||||||
unsafe { std::mem::transmute(r) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the height of this Scope - IE the number of scopes above it.
|
/// Get the height of this Scope - IE the number of scopes above it.
|
||||||
|
@ -354,21 +332,19 @@ impl<'src> ScopeState {
|
||||||
&'src self,
|
&'src self,
|
||||||
mut callback: impl FnMut(Event<T>) + 'src,
|
mut callback: impl FnMut(Event<T>) + 'src,
|
||||||
) -> AttributeValue {
|
) -> AttributeValue {
|
||||||
AttributeValue::Listener(RefCell::new(Some(Box::new(
|
AttributeValue::Listener(Box::new(move |event: Event<dyn Any>| {
|
||||||
move |event: Event<dyn Any>| {
|
if let Ok(data) = event.data.downcast::<T>() {
|
||||||
if let Ok(data) = event.data.downcast::<T>() {
|
callback(Event {
|
||||||
callback(Event {
|
propagates: event.propagates,
|
||||||
propagates: event.propagates,
|
data,
|
||||||
data,
|
});
|
||||||
});
|
}
|
||||||
}
|
}))
|
||||||
},
|
|
||||||
))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
|
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
|
||||||
pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue {
|
pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue {
|
||||||
AttributeValue::Any(RefCell::new(Some(Box::new(value))))
|
AttributeValue::Any(Box::new(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark this component as suspended and then return None
|
/// Mark this component as suspended and then return None
|
||||||
|
@ -377,47 +353,4 @@ impl<'src> ScopeState {
|
||||||
cx.suspend();
|
cx.suspend();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.");
|
|
||||||
|
|
||||||
if cur_hook >= hooks.len() {
|
|
||||||
hooks.push(Box::new(UnsafeCell::new(initializer())));
|
|
||||||
}
|
|
||||||
|
|
||||||
hooks
|
|
||||||
.get(cur_hook)
|
|
||||||
.and_then(|inn| {
|
|
||||||
self.hook_idx.set(cur_hook + 1);
|
|
||||||
let raw_ref = unsafe { &mut *inn.get() };
|
|
||||||
raw_ref.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.
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ impl VirtualDom {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Note: the VirtualDom is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
|
/// Note: the VirtualDom is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
|
||||||
pub fn new(app: fn() -> Element) -> Self {
|
pub fn new(app: fn(()) -> Element) -> Self {
|
||||||
Self::new_with_props(app, ())
|
Self::new_with_props(app, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ impl VirtualDom {
|
||||||
/// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
|
/// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
|
||||||
/// let mutations = dom.rebuild();
|
/// let mutations = dom.rebuild();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new_with_props<P: 'static>(root: fn(P) -> Element, root_props: P) -> Self {
|
pub fn new_with_props<P: Clone + 'static>(root: fn(P) -> Element, root_props: P) -> Self {
|
||||||
let (tx, rx) = futures_channel::mpsc::unbounded();
|
let (tx, rx) = futures_channel::mpsc::unbounded();
|
||||||
let scheduler = Scheduler::new(tx);
|
let scheduler = Scheduler::new(tx);
|
||||||
let mut dom = Self {
|
let mut dom = Self {
|
||||||
|
@ -450,7 +450,7 @@ impl VirtualDom {
|
||||||
let origin = path.scope;
|
let origin = path.scope;
|
||||||
self.runtime.scope_stack.borrow_mut().push(origin);
|
self.runtime.scope_stack.borrow_mut().push(origin);
|
||||||
self.runtime.rendering.set(false);
|
self.runtime.rendering.set(false);
|
||||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
if let Some(cb) = listener.as_deref_mut() {
|
||||||
cb(uievent.clone());
|
cb(uievent.clone());
|
||||||
}
|
}
|
||||||
self.runtime.scope_stack.borrow_mut().pop();
|
self.runtime.scope_stack.borrow_mut().pop();
|
||||||
|
|
Loading…
Reference in a new issue