feat: code quality improvements for core

This commit is contained in:
Jonathan Kelley 2021-07-15 04:09:28 -04:00
parent 37ed4bed8c
commit 00231adfa2
12 changed files with 124 additions and 758 deletions

View file

@ -1,16 +1,11 @@
use std::{
cell::{RefCell, UnsafeCell},
collections::HashMap,
rc::Rc,
sync::Arc,
};
use std::{cell::UnsafeCell, rc::Rc};
use crate::innerlude::*;
use slotmap::{DefaultKey, SlotMap};
#[derive(Clone)]
pub struct SharedArena {
pub components: Arc<UnsafeCell<ScopeMap>>,
pub components: Rc<UnsafeCell<ScopeMap>>,
}
pub type ScopeMap = SlotMap<DefaultKey, Scope>;
@ -21,7 +16,7 @@ enum MutStatus {
impl SharedArena {
pub fn new(arena: ScopeMap) -> Self {
let components = Arc::new(UnsafeCell::new(arena));
let components = Rc::new(UnsafeCell::new(arena));
SharedArena { components }
}

View file

@ -1,15 +1,11 @@
use crate::innerlude::*;
use futures_util::FutureExt;
use std::any::Any;
use std::cell::Cell;
use std::marker::PhantomData;
use std::{
any::TypeId,
cell::RefCell,
any::{Any, TypeId},
cell::{Cell, RefCell},
future::Future,
marker::PhantomData,
ops::Deref,
rc::{Rc, Weak},
};
@ -316,23 +312,7 @@ Any function prefixed with "use" should not be called conditionally.
///
///
pub fn submit_task(&self, task: FiberTask) -> TaskHandle {
let r = (self.scope.task_submitter)(task);
// self.scope.submit_task(task);
// let r = task.then(|f| async {
// //
// });
// self.use_hook(|| Box::new(r), |_| {}, |_| {});
// *task = task.then(|f| async {
// //
// t
// // ()
// });
// let new_fut = task.then(|f| async {
// //
// ()
// });
// self.tasks.borrow_mut().push(new_fut);
(self.scope.task_submitter)(task);
TaskHandle { _p: PhantomData {} }
}

View file

@ -76,35 +76,3 @@ impl DebugRenderer {
Ok(())
}
}
pub struct DebugVNodeSource {
bump: Bump,
}
impl DebugVNodeSource {
fn new() -> Self {
Self { bump: Bump::new() }
}
fn render_nodes(&self) -> VNode {
// let cx = NodeFactory
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_creation() -> Result<(), ()> {
// static Example: FC<()> = |cx| {
// //
// cx.render(html! { <div> "hello world" </div> })
// };
// let mut dom = VirtualDom::new(Example);
// let machine = DiffMachine::new();
Ok(())
}
}

View file

@ -12,7 +12,7 @@ use crate::{innerlude::ScopeId, RealDomNode};
/// However, the DomEditor only builds a change list - it does not apply them. In contrast with the "RealDom", the DomEditor
/// is cancellable and flushable. At any moment in time, Dioxus may choose to completely clear the edit list and start over.
///
/// This behavior is used in the cooperative scheduling algorithm
/// This behavior is used in the cooperative scheduling algorithm.
pub struct DomEditor<'real, 'bump> {
pub edits: &'real mut Vec<DomEdit<'bump>>,
}

View file

@ -26,6 +26,7 @@ pub mod prelude {
// types used internally that are important
pub(crate) mod innerlude {
pub use crate::arena::*;
pub use crate::bumpframe::*;
pub use crate::component::*;
pub use crate::context::*;
@ -33,6 +34,7 @@ pub(crate) mod innerlude {
pub use crate::editor::*;
pub use crate::error::*;
pub use crate::events::*;
pub use crate::hooklist::*;
pub use crate::nodes::*;
pub use crate::scope::*;
pub use crate::tasks::*;
@ -46,6 +48,7 @@ pub(crate) mod innerlude {
pub mod exports {
// export important things here
pub use bumpalo;
}
pub mod arena;

View file

@ -1,8 +1,8 @@
//! Virtual Node Support
//! --------------------
//! VNodes represent lazily-constructed VDom trees that support diffing and event handlers.
//!
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
use crate::{
events::VirtualEvent,
innerlude::{Context, Properties, RealDomNode, Scope, ScopeId, FC},
@ -16,8 +16,13 @@ use std::{
pub struct VNode<'src> {
pub kind: VNodeKind<'src>,
pub dom_id: Cell<RealDomNode>,
pub key: Option<&'src str>,
pub(crate) dom_id: Cell<RealDomNode>,
pub(crate) key: Option<&'src str>,
}
impl VNode<'_> {
fn key(&self) -> Option<&str> {
self.key
}
}
/// Tools for the base unit of the virtual dom - the VNode
@ -27,9 +32,13 @@ pub struct VNode<'src> {
/// limit the amount of heap allocations / overly large enum sizes.
pub enum VNodeKind<'src> {
Text(VText<'src>),
Element(&'src VElement<'src>),
Fragment(VFragment<'src>),
Component(&'src VComponent<'src>),
Suspended { node: Rc<Cell<RealDomNode>> },
}
@ -55,6 +64,7 @@ pub trait DioxusElement {
Self::NAME_SPACE
}
}
pub struct VElement<'a> {
// tag is always static
pub tag_name: &'static str,
@ -75,9 +85,13 @@ pub struct VElement<'a> {
#[derive(Clone, Debug)]
pub struct Attribute<'a> {
pub name: &'static str,
pub value: &'a str,
pub is_static: bool,
pub is_volatile: bool,
// Doesn't exist in the html spec, mostly used to denote "style" tags - could be for any type of group
pub namespace: Option<&'static str>,
}
@ -87,8 +101,11 @@ pub struct Attribute<'a> {
pub struct Listener<'bump> {
/// The type of event to listen for.
pub(crate) event: &'static str,
pub scope: ScopeId,
pub mounted_node: &'bump Cell<RealDomNode>,
pub mounted_node: &'bump mut Cell<RealDomNode>,
pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
}
@ -96,9 +113,13 @@ pub struct Listener<'bump> {
/// Only supports the functional syntax
pub struct VComponent<'src> {
pub ass_scope: Cell<Option<ScopeId>>,
pub(crate) caller: Rc<dyn Fn(&Scope) -> VNode>,
pub(crate) children: &'src [VNode<'src>],
pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
pub is_static: bool,
// a pointer into the bump arena (given by the 'src lifetime)
@ -124,20 +145,7 @@ impl<'a> NodeFactory<'a> {
&self.scope_ref.cur_frame().bump
}
pub const fn const_text(&self, text: &'static str) -> VNodeKind<'static> {
VNodeKind::Text(VText {
is_static: true,
text,
})
}
pub const fn const_fragment(&self, children: &'static [VNode<'static>]) -> VNodeKind<'static> {
VNodeKind::Fragment(VFragment {
children,
is_static: true,
})
}
/// Used in a place or two to make it easier to build vnodes from dummy text
pub fn static_text(text: &'static str) -> VNode {
VNode {
dom_id: RealDomNode::empty_cell(),
@ -149,6 +157,9 @@ impl<'a> NodeFactory<'a> {
}
}
/// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
///
/// Text that's static may be pointer compared, making it cheaper to diff
pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
match args.as_str() {
Some(static_str) => (static_str, true),
@ -162,6 +173,7 @@ impl<'a> NodeFactory<'a> {
}
/// Create some text that's allocated along with the other vnodes
///
pub fn text(&self, args: Arguments) -> VNode<'a> {
let (text, is_static) = self.raw_text(args);
VNode {
@ -171,27 +183,40 @@ impl<'a> NodeFactory<'a> {
}
}
pub fn raw_element(
&self,
_tag: &'static str,
_listeners: &[Listener],
_attributes: &[Attribute],
_children: &'a [VNode<'a>],
) -> VNode<'a> {
todo!()
}
pub fn element(
&self,
el: impl DioxusElement,
listeners: &'a [Listener<'a>],
listeners: &'a mut [Listener<'a>],
attributes: &'a [Attribute<'a>],
children: &'a [VNode<'a>],
key: Option<&'a str>,
) -> VNode<'a> {
self.raw_element(
el.tag_name(),
el.namespace(),
listeners,
attributes,
children,
key,
)
}
pub fn raw_element(
&self,
tag: &'static str,
namespace: Option<&'static str>,
listeners: &'a mut [Listener],
attributes: &'a [Attribute],
children: &'a [VNode<'a>],
key: Option<&'a str>,
) -> VNode<'a> {
// We take the references directly from the bump arena
// TODO: this code shouldn't necessarily be here of all places
// It would make more sense to do this in diffing
let mut queue = self.scope_ref.listeners.borrow_mut();
for listener in listeners {
let mounted = listener.mounted_node as *const _ as *mut _;
for listener in listeners.iter_mut() {
let mounted = listener.mounted_node as *mut _;
let callback = listener.callback as *const _ as *mut _;
queue.push((mounted, callback))
}
@ -200,14 +225,16 @@ impl<'a> NodeFactory<'a> {
dom_id: RealDomNode::empty_cell(),
key,
kind: VNodeKind::Element(self.bump().alloc(VElement {
tag_name: el.tag_name(),
namespace: el.namespace(),
static_listeners: false,
tag_name: tag,
namespace,
listeners,
static_attrs: false,
attributes,
static_children: false,
children,
// todo: wire up more constization
static_listeners: false,
static_attrs: false,
static_children: false,
})),
}
}
@ -243,12 +270,15 @@ impl<'a> NodeFactory<'a> {
&self,
component: FC<P>,
props: P,
key: Option<&'a str>, // key: NodeKey<'a>,
key: Option<&'a str>,
children: &'a [VNode<'a>],
) -> VNode<'a>
where
P: Properties + 'a,
{
// TODO
// It's somewhat wrong to go about props like this
// We don't want the fat part of the fat pointer
// This function does static dispatch so we don't need any VTable stuff
let props = self.bump().alloc(props);
@ -271,6 +301,8 @@ impl<'a> NodeFactory<'a> {
}
}));
let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
VNode {
key,
dom_id: Cell::new(RealDomNode::empty()),
@ -280,7 +312,7 @@ impl<'a> NodeFactory<'a> {
raw_props,
children,
caller: NodeFactory::create_component_caller(component, raw_props),
is_static: children.len() == 0 && P::IS_STATIC && key.is_none(),
is_static,
ass_scope: Cell::new(None),
})),
}
@ -313,10 +345,26 @@ impl<'a> NodeFactory<'a> {
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
) -> VNode<'a> {
let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
// TODO throw an error if there are nodes without keys
for node in node_iter.into_iter() {
nodes.push(node.into_vnode(self));
}
if cfg!(debug_assertions) {
if nodes.len() > 1 {
if nodes.last().unwrap().key().is_none() {
log::error!(
r#"
Warning: Each child in an array or iterator should have a unique "key" prop.
Not providing a key will lead to poor performance with lists.
See docs.rs/dioxus for more information.
---
To help you identify where this error is coming from, we've generated a backtrace.
"#,
);
}
}
}
VNode {
dom_id: RealDomNode::empty_cell(),
key: None,

View file

@ -1,484 +0,0 @@
/// Typically constructed with element-specific constructors, eg the `div`
/// function for building `<div>` elements or the `button` function for building
/// `<button>` elements.
#[derive(Debug)]
pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[VNode<'a>]>,
{
cx: &'b NodeFactory<'a>,
key: NodeKey<'a>,
tag_name: &'static str,
listeners: Listeners,
attributes: Attributes,
children: Children,
namespace: Option<&'static str>,
}
impl<'a, 'b>
ElementBuilder<
'a,
'b,
bumpalo::collections::Vec<'a, Listener<'a>>,
bumpalo::collections::Vec<'a, Attribute<'a>>,
bumpalo::collections::Vec<'a, VNode<'a>>,
>
{
/// Create a new `ElementBuilder` for an element with the given tag name.
///
/// In general, only use this constructor if the tag is dynamic (i.e. you
/// might build a `<div>` or you might build a `<span>` and you don't know
/// until runtime). Prefer using the tag-specific constructors instead:
/// `div(bump)` or `span(bump)`, etc.
///
/// # Example
///
/// ```
/// use dioxus::{builder::*, bumpalo::Bump};
///
/// let b = Bump::new();
///
/// let tag_name = if flip_coin() {
/// "div"
/// } else {
/// "span"
/// };
///
/// let my_element_builder = ElementBuilder::new(&b, tag_name);
/// # fn flip_coin() -> bool { true }
/// ```
pub fn new(cx: &'b NodeFactory<'a>, tag_name: &'static str) -> Self {
let bump = cx.bump();
ElementBuilder {
cx,
key: NodeKey::NONE,
tag_name,
listeners: bumpalo::collections::Vec::new_in(bump),
attributes: bumpalo::collections::Vec::new_in(bump),
children: bumpalo::collections::Vec::new_in(bump),
namespace: None,
}
}
}
impl<'a, 'b, Listeners, Attributes, Children>
ElementBuilder<'a, 'b, Listeners, Attributes, Children>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[VNode<'a>]>,
{
/// Set the listeners for this element.
///
/// You can use this method to customize the backing storage for listeners,
/// for example to use a fixed-size array instead of the default
/// dynamically-sized `bumpalo::collections::Vec`.
///
/// Any listeners already added to the builder will be overridden.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump};
///
/// let b = Bump::new();
///
/// // Create a `<div>` with a fixed-size array of two listeners.
/// let my_div = div(&b)
/// .listeners([
/// on(&b, "click", |root, vdom, event| {
/// // ...
/// }),
/// on(&b, "dblclick", |root, vdom, event| {
/// // ...
/// }),
/// ])
/// .finish();
/// ```
#[inline]
pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
where
L: 'a + AsRef<[Listener<'a>]>,
{
ElementBuilder {
cx: self.cx,
key: self.key,
tag_name: self.tag_name,
listeners,
attributes: self.attributes,
children: self.children,
namespace: self.namespace,
}
}
/// Set the attributes for this element.
///
/// You can use this method to customize the backing storage for attributes,
/// for example to use a fixed-size array instead of the default
/// dynamically-sized `bumpalo::collections::Vec`.
///
/// Any attributes already added to the builder will be overridden.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump, Attribute};
///
/// let b = Bump::new();
///
/// // Create a `<div>` with a fixed-size array of two attributes.
/// let my_div = div(&b)
/// .attributes([
/// attr("id", "my-div"),
/// attr("class", "notification"),
/// ])
/// .finish();
/// ```
#[inline]
pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
where
A: 'a + AsRef<[Attribute<'a>]>,
{
ElementBuilder {
cx: self.cx,
key: self.key,
tag_name: self.tag_name,
listeners: self.listeners,
attributes,
children: self.children,
namespace: self.namespace,
}
}
/// Set the children for this element.
///
/// You can use this method to customize the backing storage for children,
/// for example to use a fixed-size array instead of the default
/// dynamically-sized `bumpalo::collections::Vec`.
///
/// Any children already added to the builder will be overridden.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump};
///
/// let b = Bump::new();
///
/// // Create a `<div>` with a fixed-size array of two `<span>` children.
/// let my_div = div(&b)
/// .children([
/// span(&b).finish(),
/// span(&b).finish(),
/// ])
/// .finish();
/// ```
#[inline]
pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
where
C: 'a + AsRef<[VNode<'a>]>,
{
ElementBuilder {
cx: self.cx,
key: self.key,
tag_name: self.tag_name,
listeners: self.listeners,
attributes: self.attributes,
children,
namespace: self.namespace,
}
}
/// Set the namespace for this element.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump};
///
/// let b = Bump::new();
///
/// // Create a `<td>` tag with an xhtml namespace
/// let my_td = td(&b)
/// .namespace(Some("http://www.w3.org/1999/xhtml"))
/// .finish();
/// ```
#[inline]
pub fn namespace(self, namespace: Option<&'static str>) -> Self {
ElementBuilder {
cx: self.cx,
key: self.key,
tag_name: self.tag_name,
listeners: self.listeners,
attributes: self.attributes,
children: self.children,
namespace,
}
}
/// Set this element's key.
///
/// When diffing sets of siblings, if an old sibling and new sibling share a
/// key, then they will always reuse the same physical DOM VNode. This is
/// important when using CSS animations, web components, third party JS, or
/// anything else that makes the diffing implementation observable.
///
/// Do not use keys if such a scenario does not apply. Keyed diffing is
/// generally more expensive than not, since it is putting greater
/// constraints on the diffing algorithm.
///
/// # Invariants You Must Uphold
///
/// The key may not be `u32::MAX`, which is a reserved key value.
///
/// Keys must be unique among siblings.
///
/// All sibling VNodes must be keyed, or they must all not be keyed. You may
/// not mix keyed and unkeyed siblings.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump};
///
/// let b = Bump::new();
///
/// let my_li = li(&b)
/// .key(1337)
/// .finish();
/// ```
#[inline]
pub fn key(mut self, key: &'a str) -> Self {
self.key = NodeKey(Some(key));
self
}
pub fn key2(mut self, args: Arguments) -> Self {
let key = match args.as_str() {
Some(static_str) => static_str,
None => {
use bumpalo::core_alloc::fmt::Write;
let mut s = bumpalo::collections::String::new_in(self.cx.bump());
s.write_fmt(args).unwrap();
s.into_bump_str()
}
};
self.key = NodeKey(Some(key));
self
}
/// Create the virtual DOM VNode described by this builder.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump, VNode};
///
/// let b = Bump::new();
///
/// // Start with a builder...
/// let builder: ElementBuilder<_, _, _> = div(&b);
///
/// // ...and finish it to create a virtual DOM VNode!
/// let my_div: VNode = builder.finish();
/// ```
#[inline]
pub fn finish(mut self) -> VNode<'a> {
let bump = self.cx.bump();
let children: &'a Children = bump.alloc(self.children);
let children: &'a [VNode<'a>] = children.as_ref();
let listeners: &'a Listeners = bump.alloc(self.listeners);
let listeners: &'a [Listener<'a>] = listeners.as_ref();
for listener in listeners {
// bump the context id forward
let id = self.cx.listener_id.get();
self.cx.listener_id.set(id + 1);
// Add this listener to the context list
// This casts the listener to a self-referential pointer
// This is okay because the bump arena is stable
// TODO: maybe not add it into CTX immediately
let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(listener) };
self.cx.scope_ref.listeners.borrow_mut().push((
r.mounted_node as *const _ as *mut _,
r.callback as *const _ as *mut _,
));
}
let attributes: &'a Attributes = bump.alloc(self.attributes);
let attributes: &'a [Attribute<'a>] = attributes.as_ref();
VNode::element(
bump,
self.key,
self.tag_name,
listeners,
attributes,
children,
self.namespace,
)
}
}
impl<'a, 'b, Attributes, Children>
ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
where
Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[VNode<'a>]>,
{
// / Add a new event listener to this element.
// /
// / The `event` string specifies which event will be listened for. The
// / `callback` function is the function that will be invoked if the
// / specified event occurs.
// /
// / # Example
// /
// / ```no_run
// / use dioxus::{builder::*, bumpalo::Bump};
// /
// / let b = Bump::new();
// /
// / // A button that does something when clicked!
// / let my_button = button(&b)
// / .on("click", |event| {
// / // ...
// / })
// / .finish();
// / ```
// pub fn on(self, event: &'static str, callback: impl FnMut(VirtualEvent) + 'a) -> Self {
// let bump = &self.cx.bump();
// let listener = Listener {
// event,
// callback: bump.alloc(callback),
// scope: self.cx.scope_ref.arena_idx,
// mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
// };
// self.add_listener(listener)
// }
// pub fn add_listener(mut self, listener: Listener<'a>) -> Self {
// self.listeners.push(listener);
// // bump the context id forward
// let id = self.cx.listener_id.get();
// self.cx.listener_id.set(id + 1);
// // Add this listener to the context list
// // This casts the listener to a self-referential pointer
// // This is okay because the bump arena is stable
// self.listeners.last().map(|g| {
// let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(g) };
// self.cx.scope_ref.listeners.borrow_mut().push((
// r.mounted_node as *const _ as *mut _,
// r.callback as *const _ as *mut _,
// ));
// });
// self
// }
}
impl<'a, 'b, Listeners, Children>
ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Children: 'a + AsRef<[VNode<'a>]>,
{
/// Add a new attribute to this element.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump};
///
/// let b = Bump::new();
///
/// // Create the `<div id="my-div"/>` element.
/// let my_div = div(&b).attr("id", "my-div").finish();
/// ```
pub fn attr(mut self, name: &'static str, args: std::fmt::Arguments) -> Self {
let (value, is_static) = raw_text(self.cx.bump(), args);
self.attributes.push(Attribute {
name,
value,
is_static,
namespace: None,
});
self
}
}
impl<'a, 'b, Listeners, Attributes>
ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>,
{
/// Add a new child to this element.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump};
/// use js_sys::Math;
///
/// let b = Bump::new();
///
/// // Create `<p><span></span></p>`.
/// let my_div = p(&b)
/// .child(span(&b).finish())
/// .finish();
/// ```
#[inline]
pub fn child(mut self, child: VNode<'a>) -> Self {
self.children.push(child);
self
}
/// Add multiple children to this element from an iterator.
///
/// # Example
///
/// ```no_run
/// use dioxus::{builder::*, bumpalo::Bump};
///
/// let b = Bump::new();
///
/// let my_div = p(&b)
/// .iter_child((0..10).map(|f| span(&b).finish())
/// .finish();
/// ```
pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
todo!();
let len_before = self.children.len();
for item in nodes {
todo!()
// let child = item.into_vnode(&self.cx);
// self.children.push(child);
}
if cfg!(debug_assertions) {
if self.children.len() > len_before + 1 {
if self.children.last().unwrap().key().is_none() {
log::error!(
r#"
Warning: Each child in an array or iterator should have a unique "key" prop.
Not providing a key will lead to poor performance with lists.
See docs.rs/dioxus for more information.
---
To help you identify where this error is coming from, we've generated a backtrace.
"#,
);
}
}
}
self
}
}

View file

@ -1,6 +1,4 @@
use crate::hooklist::HookList;
use crate::{arena::SharedArena, innerlude::*};
use crate::innerlude::*;
use std::{
any::{Any, TypeId},
cell::{Cell, RefCell},
@ -10,17 +8,15 @@ use std::{
rc::Rc,
};
// We need to pin the hook so it doesn't move as we initialize the list of hooks
type Hook = Box<dyn std::any::Any>;
type EventChannel = Rc<dyn Fn()>;
pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> VNode<'b>;
/// Every component in Dioxus is represented by a `Scope`.
///
/// Scopes contain the state for hooks, the component's props, and other lifecycle information.
///
/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
///
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
/// usecase they might have.
pub struct Scope {
// Book-keeping about the arena
pub(crate) parent_idx: Option<ScopeId>,
@ -47,13 +43,19 @@ pub struct Scope {
// Tasks
pub(crate) task_submitter: TaskSubmitter,
pub(crate) suspended_tasks: Vec<*mut Pin<Box<dyn Future<Output = VNode<'static>>>>>,
// A reference to the list of components.
// This lets us traverse the component list whenever we need to access our parent or children.
pub(crate) arena_link: SharedArena,
}
// The type of the channel function
type EventChannel = Rc<dyn Fn()>;
// The type of closure that wraps calling components
pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> VNode<'b>;
// The type of task that gets sent to the task scheduler
pub type FiberTask = Pin<Box<dyn Future<Output = EventTrigger>>>;
impl Scope {
@ -74,14 +76,7 @@ impl Scope {
child_nodes: &'creator_node [VNode<'creator_node>],
task_submitter: TaskSubmitter,
) -> Self {
log::debug!(
"New scope created, height is {}, idx is {:?}",
height,
arena_idx
);
let child_nodes = unsafe { std::mem::transmute(child_nodes) };
Self {
child_nodes,
caller,
@ -97,15 +92,14 @@ impl Scope {
shared_contexts: Default::default(),
listeners: Default::default(),
descendents: Default::default(),
suspended_tasks: Default::default(),
}
}
pub fn update_caller<'creator_node>(&mut self, caller: Rc<WrappedCaller>) {
pub(crate) fn update_caller<'creator_node>(&mut self, caller: Rc<WrappedCaller>) {
self.caller = caller;
}
pub fn update_children<'creator_node>(
pub(crate) fn update_children<'creator_node>(
&mut self,
child_nodes: &'creator_node [VNode<'creator_node>],
) {
@ -113,13 +107,14 @@ impl Scope {
self.child_nodes = child_nodes;
}
pub fn run_scope<'sel>(&'sel mut self) -> Result<()> {
pub(crate) fn run_scope<'sel>(&'sel mut self) -> Result<()> {
// 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
// This is a very dangerous operation
self.frames.next().bump.reset();
log::debug!("clearing listeners!");
// Remove all the outdated listeners
self.listeners.borrow_mut().clear();
unsafe { self.hooks.reset() };
@ -133,17 +128,6 @@ impl Scope {
Ok(())
}
/// Progress a suspended node
pub fn progress_suspended(&mut self) -> Result<()> {
// load the hook
// downcast to our special state
// run just this hook
// create a new vnode
// diff this new vnode with the original suspended vnode
Ok(())
}
// this is its own function so we can preciesly control how lifetimes flow
unsafe fn call_user_component<'a>(&'a self, caller: &WrappedCaller) -> VNode<'static> {
let new_head: VNode<'a> = caller(self);
@ -153,7 +137,7 @@ impl Scope {
// A safe wrapper around calling listeners
// calling listeners will invalidate the list of listeners
// The listener list will be completely drained because the next frame will write over previous listeners
pub fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
pub(crate) fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
let EventTrigger {
real_node_id,
event,
@ -165,11 +149,6 @@ impl Scope {
return Ok(());
}
// todo: implement scanning for outdated events
// Convert the raw ptr into an actual object
// This operation is assumed to be safe
log::debug!(
"There are {:?} listeners associated with this scope {:#?}",
self.listeners.borrow().len(),
@ -178,7 +157,6 @@ impl Scope {
let listners = self.listeners.borrow_mut();
// let listener = listners.get(trigger);
let raw_listener = listners.iter().find(|(domptr, _)| {
let search = unsafe { &**domptr };
let search_id = search.get();
@ -201,7 +179,7 @@ impl Scope {
Ok(())
}
pub fn submit_task(&self, task: FiberTask) {
pub(crate) fn submit_task(&self, task: FiberTask) {
log::debug!("Task submitted into scope");
(self.task_submitter)(task);
}
@ -221,6 +199,7 @@ impl Scope {
self.frames.cur_frame()
}
/// Get the root VNode of this component
#[inline]
pub fn root<'a>(&'a self) -> &'a VNode<'a> {
&self.frames.cur_frame().head_node

View file

@ -2,7 +2,4 @@
//! -----------------------------
//!
//!
//!
//!
//!
//!
//! TODO!

View file

@ -135,45 +135,3 @@ impl DTask {
}
}
}
// #[cfg(test)]
// mod tests {
// use std::time::Duration;
// use super::*;
// use bumpalo::Bump;
// #[async_std::test]
// async fn example() {
// let bump = Bump::new();
// type RawTask = Pin<Box<dyn Future<Output = ()>>>;
// // build the three
// let f1 = bump.alloc(Box::pin(async {
// //
// async_std::task::sleep(Duration::from_secs(3)).await;
// println!("3 sec")
// }) as RawTask);
// let f2 = bump.alloc(Box::pin(async {
// //
// async_std::task::sleep(Duration::from_secs(2)).await;
// println!("2 sec")
// }) as RawTask);
// let f3 = bump.alloc(Box::pin(async {
// //
// async_std::task::sleep(Duration::from_secs(1)).await;
// println!("1 sec");
// }) as RawTask);
// let mut queue = TaskQueue::new();
// queue.submit_task(DTask::debug_new(f1));
// queue.submit_task(DTask::debug_new(f2));
// queue.submit_task(DTask::debug_new(f3));
// while !queue.is_empty() {
// let next = queue.next().await;
// println!("Event received {:#?}", next);
// }
// }
// }

View file

@ -63,8 +63,6 @@ pub struct RealDomNode(pub u64);
impl RealDomNode {
#[inline]
pub fn empty() -> Self {
// let data = KeyData::from_ffi(u64::MIN);
// let key: DefaultKey = data.into();
Self(u64::MIN)
}
#[inline]
@ -73,101 +71,31 @@ impl RealDomNode {
}
#[inline]
pub fn from_u64(id: u64) -> Self {
// let data = KeyData::from_ffi(id);
// let key: DefaultKey = data.into();
Self(id)
}
#[inline]
pub fn as_u64(&self) -> u64 {
// self.0.data().as_ffi()
self.0
}
}
pub struct DebugDom {
counter: u64,
logging: bool,
}
impl DebugDom {
pub fn new() -> Self {
Self {
counter: 0,
logging: false,
}
}
pub fn with_logging_enabled() -> Self {
Self {
counter: 0,
logging: true,
}
Self { counter: 0 }
}
}
impl<'a> RealDom<'a> for DebugDom {
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
todo!()
}
fn request_available_node(&mut self) -> RealDomNode {
todo!()
self.counter += 1;
RealDomNode::from_u64(self.counter)
}
}
async fn launch_demo(app: FC<()>) {
let mut dom = VirtualDom::new(app);
let mut real_dom = DebugDom::new();
let mut edits = Vec::new();
dom.rebuild(&mut real_dom, &mut edits).unwrap();
while let Some(evt) = dom.tasks.next().await {
//
log::debug!("Event triggered! {:#?}", evt);
}
}
// #[cfg(test)]
// mod tests {
// use super::*;
// use crate as dioxus;
// use std::{pin::Pin, time::Duration};
// use crate::builder::DioxusElement;
// use dioxus::prelude::*;
// use futures::Future;
// #[async_std::test]
// async fn async_tick() {
// static App: FC<()> = |cx| {
// // let mut count = use_state(cx, || 0);
// let mut fut = cx.use_hook(
// move || {
// Box::pin(async {
// //
// let mut tick: i32 = 0;
// loop {
// async_std::task::sleep(Duration::from_millis(250)).await;
// log::debug!("ticking forward... {}", tick);
// tick += 1;
// // match surf::get(ENDPOINT).recv_json::<DogApi>().await {
// // Ok(_) => (),
// // Err(_) => (),
// // }
// }
// }) as Pin<Box<dyn Future<Output = ()> + 'static>>
// },
// |h| h,
// |_| {},
// );
// cx.submit_task(fut);
// cx.render(LazyNodes::new(move |f| {
// f.text(format_args!("it's sorta working"))
// }))
// };
// std::env::set_var("RUST_LOG", "debug");
// env_logger::init();
// launch_demo(App).await;
// }
// }

View file

@ -120,13 +120,7 @@ impl<'a> TextRenderer<'a> {
VNodeKind::Component(vcomp) => {
let idx = vcomp.ass_scope.get().unwrap();
let new_node = self
.vdom
.components
.try_get(idx)
.unwrap()
.frames
.current_head_node();
let new_node = self.vdom.components.try_get(idx).unwrap().root();
self.html_render(new_node, f, il + 1)?;
}