wip: pre any props

This commit is contained in:
Jonathan Kelley committed 2022-11-19 17:07:29 -08:00
1 parent 12808ec0aa
commit 493591400f
14 files changed
+399 -381

No files matched your search

+1 -4
View File
@@ -13,10 +13,7 @@ pub trait AnyProps<'a> {
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
}
pub(crate) struct VComponentProps<'a, P, A, F = Element<'a>>
where
F: ComponentReturn<'a, A>,
{
pub(crate) struct VComponentProps<'a, P, A, F: ComponentReturn<'a, A> = Element<'a>> {
pub render_fn: fn(Scope<'a, P>) -> F,
pub memo: unsafe fn(&P, &P) -> bool,
pub props: P,
+3 -3
View File
@@ -1,13 +1,12 @@
use std::cell::Cell;
use bumpalo::Bump;
use crate::factory::RenderReturn;
use bumpalo::Bump;
pub struct BumpFrame {
pub bump: Bump,
pub node: Cell<*mut RenderReturn<'static>>,
}
impl BumpFrame {
pub fn new(capacity: usize) -> Self {
let bump = Bump::with_capacity(capacity);
@@ -22,6 +21,7 @@ impl BumpFrame {
self.node.set(std::ptr::null_mut());
}
/// Creates a new lifetime out of thin air
pub unsafe fn load_node<'b>(&self) -> &'b RenderReturn<'b> {
unsafe { std::mem::transmute(&*self.node.get()) }
}
+15 -18
View File
@@ -1,5 +1,5 @@
use crate::factory::RenderReturn;
use crate::innerlude::{Mutations, SuspenseContext};
use crate::innerlude::{Mutations, SuspenseContext, VText};
use crate::mutations::Mutation;
use crate::mutations::Mutation::*;
use crate::nodes::VNode;
@@ -70,9 +70,11 @@ impl VirtualDom {
&template.dynamic_nodes[*id],
*id,
),
DynamicNode::Text {
id: slot, value, ..
} => {
DynamicNode::Text(VText {
id: slot,
value,
inner,
}) => {
let id = self.next_element(template, template.template.node_paths[*id]);
slot.set(id);
mutations.push(CreateTextNode { value, id });
@@ -237,7 +239,7 @@ impl VirtualDom {
idx: usize,
) -> usize {
match &node {
DynamicNode::Text { id, value, .. } => {
DynamicNode::Text(VText { id, value, inner }) => {
let new_id = self.next_element(template, template.template.node_paths[idx]);
id.set(new_id);
mutations.push(HydrateText {
@@ -248,17 +250,12 @@ impl VirtualDom {
0
}
DynamicNode::Component {
props,
placeholder,
scope: scope_slot,
..
} => {
DynamicNode::Component(component) => {
let scope = self
.new_scope(unsafe { std::mem::transmute(props.get()) })
.new_scope(unsafe { std::mem::transmute(component.props.get()) })
.id;
scope_slot.set(Some(scope));
component.scope.set(Some(scope));
let return_nodes = unsafe { self.run_scope(scope).extend_lifetime_ref() };
@@ -269,7 +266,7 @@ impl VirtualDom {
RenderReturn::Async(_) => {
let new_id = self.next_element(template, template.template.node_paths[idx]);
placeholder.set(Some(new_id));
component.placeholder.set(Some(new_id));
self.scopes[scope.0].placeholder.set(Some(new_id));
mutations.push(AssignId {
@@ -306,7 +303,7 @@ impl VirtualDom {
// Since this is a boundary, use it as a placeholder
let new_id =
self.next_element(template, template.template.node_paths[idx]);
placeholder.set(Some(new_id));
component.placeholder.set(Some(new_id));
self.scopes[scope.0].placeholder.set(Some(new_id));
mutations.push(AssignId {
id: new_id,
@@ -342,9 +339,9 @@ impl VirtualDom {
}
}
DynamicNode::Fragment { nodes, .. } => {
//
nodes
DynamicNode::Fragment(frag) => {
// Todo: if there's no children create a placeholder instead ?
frag.nodes
.iter()
.fold(0, |acc, child| acc + self.create(mutations, child))
}
+188 -168
View File
@@ -1,7 +1,7 @@
use std::any::Any;
use crate::factory::RenderReturn;
use crate::innerlude::Mutations;
use crate::innerlude::{Mutations, VComponent, VFragment, VText};
use crate::virtual_dom::VirtualDom;
use crate::{Attribute, AttributeValue, TemplateNode};
@@ -42,16 +42,12 @@ impl<'b> VirtualDom {
pub fn diff_scope(&mut self, mutations: &mut Mutations<'b>, scope: ScopeId) {
let scope_state = &mut self.scopes[scope.0];
let cur_arena = scope_state.previous_frame();
let prev_arena = scope_state.current_frame();
// let cur_arena = scope_state.current_frame();
// let prev_arena = scope_state.previous_frame();
// relax the borrow checker
let cur_arena: &BumpFrame = unsafe { std::mem::transmute(cur_arena) };
let prev_arena: &BumpFrame = unsafe { std::mem::transmute(prev_arena) };
// Load the old and new bump arenas
let cur_arena = scope_state.current_frame();
let prev_arena = scope_state.previous_frame();
// Make sure the nodes arent null (they've been set properly)
// This is a rough check to make sure we're not entering any UB
assert_ne!(
cur_arena.node.get(),
std::ptr::null_mut(),
@@ -64,11 +60,11 @@ impl<'b> VirtualDom {
);
self.scope_stack.push(scope);
let left = unsafe { prev_arena.load_node() };
let right = unsafe { cur_arena.load_node() };
self.diff_maybe_node(mutations, left, right);
unsafe {
let cur_arena = cur_arena.load_node();
let prev_arena = prev_arena.load_node();
self.diff_maybe_node(mutations, prev_arena, cur_arena);
}
self.scope_stack.pop();
}
@@ -109,10 +105,9 @@ impl<'b> VirtualDom {
left_template: &'b VNode<'b>,
right_template: &'b VNode<'b>,
) {
println!("diffing {:?} and {:?}", left_template, right_template);
if left_template.template.id != right_template.template.id {
// do a light diff of the roots nodes.
todo!("lightly diff roots and replace");
return;
}
@@ -122,23 +117,21 @@ impl<'b> VirtualDom {
.zip(right_template.dynamic_attrs.iter())
.enumerate()
{
debug_assert!(left_attr.name == right_attr.name);
debug_assert!(left_attr.value == right_attr.value);
// Move over the ID from the old to the new
right_attr
.mounted_element
.set(left_attr.mounted_element.get());
if left_attr.value != right_attr.value {
println!("DIFF ATTR: {:?} -> {:?}", left_attr, right_attr);
let value = "todo!()";
muts.push(Mutation::SetAttribute {
id: left_attr.mounted_element.get(),
name: left_attr.name,
value,
ns: right_attr.namespace,
});
// todo: add more types of attribute values
if let AttributeValue::Text(text) = right_attr.value {
muts.push(Mutation::SetAttribute {
id: left_attr.mounted_element.get(),
name: left_attr.name,
value: text,
ns: right_attr.namespace,
});
}
}
}
@@ -148,102 +141,188 @@ impl<'b> VirtualDom {
.zip(right_template.dynamic_nodes.iter())
.enumerate()
{
#[rustfmt::skip]
use DynamicNode::*;
match (left_node, right_node) {
(DynamicNode::Component { props: lprops, .. }, DynamicNode::Component { static_props: is_static , props: rprops, .. }) => {
let left_props = unsafe { &mut *lprops.get()};
let right_props = unsafe { &mut *rprops.get()};
(Component(left), Component(right)) => self.diff_vcomponent(muts, left, right),
(Text(left), Text(right)) => self.diff_vtext(muts, left, right),
(Fragment(left), Fragment(right)) => self.diff_vfragment(muts, left, right),
// Ensure these two props are of the same component type
match left_props.as_ptr() == right_props.as_ptr() {
true => {
//
(Placeholder(_), Placeholder(_)) => todo!(),
if *is_static {
let props_are_same = unsafe { left_props.memoize(right_props) };
if props_are_same{
//
} else {
//
}
} else {
}
},
false => todo!(),
}
//
},
// Make sure to drop all the fragment children properly
(DynamicNode::Fragment { .. }, right) => todo!(),
(Text(left), right) => {
todo!()
// let m = self.create_dynamic_node(muts, right_template, right, idx);
// muts.push(Mutation::ReplaceWith { id: lid.get(), m });
}
(Placeholder(_), _) => todo!(),
// Make sure to drop the component properly
(DynamicNode::Component { .. }, right) => {
(Component { .. }, right) => {
// remove all the component roots except for the first
// replace the first with the new node
let m = self.create_dynamic_node(muts, right_template, right, idx);
todo!()
},
(DynamicNode::Text { id: lid, value: lvalue, .. }, DynamicNode::Text { id: rid, value: rvalue, .. }) => {
rid.set(lid.get());
if lvalue != rvalue {
muts.push(Mutation::SetText {
id: lid.get(),
value: rvalue,
});
}
},
(DynamicNode::Text { id: lid, .. }, right) => {
let m = self.create_dynamic_node(muts, right_template, right, idx);
muts.push(Mutation::ReplaceWith { id: lid.get(), m });
}
(DynamicNode::Placeholder(_), DynamicNode::Placeholder(_)) => todo!(),
(DynamicNode::Placeholder(_), _) => todo!(),
(DynamicNode::Fragment { nodes: lnodes, ..}, DynamicNode::Fragment { nodes: rnodes, ..}) => {
// match (old, new) {
// ([], []) => rp.set(lp.get()),
// ([], _) => {
// //
// todo!()
// },
// (_, []) => {
// todo!()
// },
// _ => {
// let new_is_keyed = new[0].key.is_some();
// let old_is_keyed = old[0].key.is_some();
// debug_assert!(
// new.iter().all(|n| n.key.is_some() == new_is_keyed),
// "all siblings must be keyed or all siblings must be non-keyed"
// );
// debug_assert!(
// old.iter().all(|o| o.key.is_some() == old_is_keyed),
// "all siblings must be keyed or all siblings must be non-keyed"
// );
// if new_is_keyed && old_is_keyed {
// self.diff_keyed_children(muts, old, new);
// } else {
// self.diff_non_keyed_children(muts, old, new);
// }
// }
// }
},
// Make sure to drop all the fragment children properly
(DynamicNode::Fragment { .. }, right) => todo!(),
};
}
}
fn diff_vcomponent(
&mut self,
muts: &mut Mutations<'b>,
left: &'b VComponent<'b>,
right: &'b VComponent<'b>,
) {
let left_props = unsafe { &mut *left.props.get() };
let right_props = unsafe { &mut *right.props.get() };
// Ensure these two props are of the same component type
match left_props.as_ptr() == right_props.as_ptr() {
true => {
//
if left.static_props {
let props_are_same = unsafe { left_props.memoize(right_props) };
if props_are_same {
//
} else {
//
}
} else {
}
}
false => todo!(),
}
}
/// Lightly diff the two templates, checking only their roots.
///
/// The goal here is to preserve any existing component state that might exist. This is to preserve some React-like
/// behavior where the component state is preserved when the component is re-rendered.
///
/// This is implemented by iterating each root, checking if the component is the same, if it is, then diff it.
///
/// We then pass the new template through "create" which should be smart enough to skip roots.
///
/// Currently, we only handle the case where the roots are the same component list. If there's any sort of deviation,
/// IE more nodes, less nodes, different nodes, or expressions, then we just replace the whole thing.
///
/// This is mostly implemented to help solve the issue where the same component is rendered under two different
/// conditions:
///
/// ```rust
/// if enabled {
/// rsx!{ Component { enabled_sign: "abc" } }
/// } else {
/// rsx!{ Component { enabled_sign: "xyz" } }
/// }
/// ```
///
/// However, we should not that it's explicit in the docs that this is not a guarantee. If you need to preserve state,
/// then you should be passing in separate props instead.
///
/// ```
///
/// let props = if enabled {
/// ComponentProps { enabled_sign: "abc" }
/// } else {
/// ComponentProps { enabled_sign: "xyz" }
/// };
///
/// rsx! {
/// Component { ..props }
/// }
/// ```
fn light_diff_tempaltes(
&mut self,
muts: &mut Mutations<'b>,
left: &'b VNode<'b>,
right: &'b VNode<'b>,
) -> bool {
if left.template.roots.len() != right.template.roots.len() {
return false;
}
let mut components = vec![];
// run through the components, ensuring they're the same
for (l, r) in left.template.roots.iter().zip(right.template.roots.iter()) {
match (l, r) {
(TemplateNode::Dynamic(l), TemplateNode::Dynamic(r)) => {
components.push(match (&left.dynamic_nodes[*l], &right.dynamic_nodes[*r]) {
(DynamicNode::Component(l), DynamicNode::Component(r)) => {
let l_props = unsafe { &*l.props.get() };
let r_props = unsafe { &*r.props.get() };
(l, r)
}
_ => return false,
})
}
_ => return false,
}
}
// run through the components, diffing them
for (l, r) in components {
self.diff_vcomponent(muts, l, r);
}
true
}
fn diff_vtext(&mut self, muts: &mut Mutations<'b>, left: &'b VText<'b>, right: &'b VText<'b>) {
right.id.set(left.id.get());
if left.value != right.value {
muts.push(Mutation::SetText {
id: left.id.get(),
value: right.value,
});
}
}
fn diff_vfragment(
&mut self,
muts: &mut Mutations<'b>,
left: &'b VFragment<'b>,
right: &'b VFragment<'b>,
) {
// match (left.nodes, right.nodes) {
// ([], []) => rp.set(lp.get()),
// ([], _) => {
// //
// todo!()
// }
// (_, []) => {
// todo!()
// }
// _ => {
// let new_is_keyed = new[0].key.is_some();
// let old_is_keyed = old[0].key.is_some();
// debug_assert!(
// new.iter().all(|n| n.key.is_some() == new_is_keyed),
// "all siblings must be keyed or all siblings must be non-keyed"
// );
// debug_assert!(
// old.iter().all(|o| o.key.is_some() == old_is_keyed),
// "all siblings must be keyed or all siblings must be non-keyed"
// );
// if new_is_keyed && old_is_keyed {
// self.diff_keyed_children(muts, old, new);
// } else {
// self.diff_non_keyed_children(muts, old, new);
// }
// }
// }
todo!()
}
// Diff children that are not keyed.
//
// The parent must be on the top of the change list stack when entering this
@@ -614,62 +693,3 @@ impl<'b> VirtualDom {
//
}
}
// /// Lightly diff the two templates and apply their edits to the dom
// fn light_diff_template_roots(
// &'a mut self,
// mutations: &mut Vec<Mutation<'a>>,
// left: &VNode,
// right: &VNode,
// ) {
// match right.template.roots.len().cmp(&left.template.roots.len()) {
// std::cmp::Ordering::Less => {
// // remove the old nodes at the end
// }
// std::cmp::Ordering::Greater => {
// // add the extra nodes.
// }
// std::cmp::Ordering::Equal => {}
// }
// for (left_node, right_node) in left.template.roots.iter().zip(right.template.roots.iter()) {
// if let (TemplateNode::Dynamic(lidx), TemplateNode::Dynamic(ridx)) =
// (left_node, right_node)
// {
// let left_node = &left.dynamic_nodes[*lidx];
// let right_node = &right.dynamic_nodes[*ridx];
// // match (left_node, right_node) {
// // (
// // DynamicNode::Component {
// // name,
// // can_memoize,
// // props,
// // },
// // DynamicNode::Component {
// // name,
// // can_memoize,
// // props,
// // },
// // ) => todo!(),
// // (
// // DynamicNode::Component {
// // name,
// // can_memoize,
// // props,
// // },
// // DynamicNode::Fragment { children },
// // ) => todo!(),
// // (
// // DynamicNode::Fragment { children },
// // DynamicNode::Component {
// // name,
// // can_memoize,
// // props,
// // },
// // ) => todo!(),
// // _ => {}
// // }
// }
// }
// }
+9 -9
View File
@@ -10,7 +10,7 @@ use std::future::Future;
use crate::{
any_props::{AnyProps, VComponentProps},
arena::ElementId,
innerlude::{DynamicNode, EventHandler},
innerlude::{DynamicNode, EventHandler, VComponent, VFragment, VText},
Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode,
};
@@ -18,11 +18,11 @@ impl ScopeState {
/// Create some text that's allocated along with the other vnodes
pub fn text<'a>(&'a self, args: Arguments) -> DynamicNode<'a> {
let (text, _) = self.raw_text(args);
DynamicNode::Text {
DynamicNode::Text(VText {
id: Cell::new(ElementId(0)),
value: text,
inner: false,
}
})
}
pub fn raw_text_inline<'a>(&'a self, args: Arguments) -> &'a str {
@@ -87,13 +87,13 @@ impl ScopeState {
// self.scope.items.borrow_mut().borrowed_props.push(vcomp);
// }
DynamicNode::Component {
DynamicNode::Component(VComponent {
name: fn_name,
static_props: P::IS_STATIC,
props: Cell::new(detached_dyn),
placeholder: Cell::new(None),
scope: Cell::new(None),
}
})
}
/// Create a new [`EventHandler`] from an [`FnMut`]
@@ -176,10 +176,10 @@ impl<'a, 'b, T: IntoDynNode<'a>> IntoDynNode<'a> for &Option<T> {
impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
DynamicNode::Fragment {
DynamicNode::Fragment(VFragment {
nodes: cx.bump().alloc([self.call(cx)]),
inner: false,
}
})
}
}
@@ -248,10 +248,10 @@ where
match children.len() {
0 => DynamicNode::Placeholder(Cell::new(ElementId(0))),
_ => DynamicNode::Fragment {
_ => DynamicNode::Fragment(VFragment {
inner: false,
nodes: children,
},
}),
}
}
}
+104
View File
@@ -0,0 +1,104 @@
use crate::innerlude::*;
/// Create inline fragments using Component syntax.
///
/// ## Details
///
/// Fragments capture a series of children without rendering extra nodes.
///
/// Creating fragments explicitly with the Fragment component is particularly useful when rendering lists or tables and
/// a key is needed to identify each item.
///
/// ## Example
///
/// ```rust, ignore
/// rsx!{
/// Fragment { key: "abc" }
/// }
/// ```
///
/// ## Usage
///
/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
/// Try to avoid highly nested fragments if you can. Unlike React, there is no protection against infinitely nested fragments.
///
/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
///
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
#[allow(non_upper_case_globals, non_snake_case)]
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
let children = cx.props.0.as_ref()?;
Some(VNode {
node_id: children.node_id.clone(),
key: children.key.clone(),
parent: children.parent.clone(),
template: children.template,
root_ids: children.root_ids,
dynamic_nodes: children.dynamic_nodes,
dynamic_attrs: children.dynamic_attrs,
})
}
pub struct FragmentProps<'a>(Element<'a>);
pub struct FragmentBuilder<'a, const BUILT: bool>(Element<'a>);
impl<'a> FragmentBuilder<'a, false> {
pub fn children(self, children: Element<'a>) -> FragmentBuilder<'a, true> {
FragmentBuilder(children)
}
}
impl<'a, const A: bool> FragmentBuilder<'a, A> {
pub fn build(self) -> FragmentProps<'a> {
FragmentProps(self.0)
}
}
/// Access the children elements passed into the component
///
/// This enables patterns where a component is passed children from its parent.
///
/// ## Details
///
/// Unlike React, Dioxus allows *only* lists of children to be passed from parent to child - not arbitrary functions
/// or classes. If you want to generate nodes instead of accepting them as a list, consider declaring a closure
/// on the props that takes Context.
///
/// If a parent passes children into a component, the child will always re-render when the parent re-renders. In other
/// words, a component cannot be automatically memoized if it borrows nodes from its parent, even if the component's
/// props are valid for the static lifetime.
///
/// ## Example
///
/// ```rust, ignore
/// fn App(cx: Scope) -> Element {
/// cx.render(rsx!{
/// CustomCard {
/// h1 {}
/// p {}
/// }
/// })
/// }
///
/// #[derive(PartialEq, Props)]
/// struct CardProps {
/// children: Element
/// }
///
/// fn CustomCard(cx: Scope<CardProps>) -> Element {
/// cx.render(rsx!{
/// div {
/// h1 {"Title card"}
/// {cx.props.children}
/// }
/// })
/// }
/// ```
impl<'a> Properties for FragmentProps<'a> {
type Builder = FragmentBuilder<'a, false>;
const IS_STATIC: bool = false;
fn builder() -> Self::Builder {
FragmentBuilder(None)
}
unsafe fn memoize(&self, _other: &Self) -> bool {
false
}
}
+11 -11
View File
@@ -17,18 +17,18 @@ impl<'b> VirtualDom {
template: &'b VNode<'b>,
gen_roots: bool,
) {
for node in template.dynamic_nodes.iter() {
match node {
DynamicNode::Text { id, .. } => {}
// for node in template.dynamic_nodes.iter() {
// match node {
// DynamicNode::Text { id, .. } => {}
DynamicNode::Component { .. } => {
todo!()
}
// DynamicNode::Component { .. } => {
// todo!()
// }
DynamicNode::Fragment { inner, nodes } => {}
DynamicNode::Placeholder(_) => todo!(),
_ => todo!(),
}
}
// DynamicNode::Fragment { inner, nodes } => {}
// DynamicNode::Placeholder(_) => todo!(),
// _ => todo!(),
// }
// }
}
}
+2
View File
@@ -5,6 +5,7 @@ mod create;
mod diff;
mod events;
mod factory;
mod fragment;
mod garbage;
mod lazynodes;
mod mutations;
@@ -18,6 +19,7 @@ mod virtual_dom;
pub(crate) mod innerlude {
pub use crate::arena::*;
pub use crate::events::*;
pub use crate::fragment::*;
pub use crate::lazynodes::*;
pub use crate::mutations::*;
pub use crate::nodes::*;
+53 -44
View File
@@ -3,28 +3,38 @@ use std::{
any::{Any, TypeId},
cell::{Cell, RefCell},
hash::Hasher,
rc::Rc,
};
pub type TemplateId = &'static str;
/// A reference to a template along with any context needed to hydrate it
///
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
/// static parts of the template.
#[derive(Debug)]
pub struct VNode<'a> {
// The ID assigned for the root of this template
pub node_id: Cell<ElementId>,
/// The key given to the root of this template.
///
/// In fragments, this is the key of the first child. In other cases, it is the key of the root.
pub key: Option<&'a str>,
// When rendered, this template will be linked to its parent manually
/// When rendered, this template will be linked to its parent manually
pub parent: Option<ElementId>,
/// The static nodes and static descriptor of the template
pub template: Template<'static>,
/// The IDs for the roots of this template - to be used when moving the template around and removing it from
/// the actual Dom
pub root_ids: &'a [Cell<ElementId>],
/// The dynamic parts of the template
pub dynamic_nodes: &'a [DynamicNode<'a>],
/// The dynamic parts of the template
pub dynamic_attrs: &'a [Attribute<'a>],
}
@@ -109,25 +119,40 @@ pub enum TemplateNode<'a> {
#[derive(Debug)]
pub enum DynamicNode<'a> {
Component {
name: &'static str,
static_props: bool,
props: Cell<*mut dyn AnyProps<'a>>,
placeholder: Cell<Option<ElementId>>,
scope: Cell<Option<ScopeId>>,
},
Text {
id: Cell<ElementId>,
value: &'a str,
inner: bool,
},
Fragment {
nodes: &'a [VNode<'a>],
inner: bool,
},
Component(VComponent<'a>),
Text(VText<'a>),
Fragment(VFragment<'a>),
Placeholder(Cell<ElementId>),
}
impl<'a> DynamicNode<'a> {
pub fn is_component(&self) -> bool {
matches!(self, DynamicNode::Component(_))
}
}
#[derive(Debug)]
pub struct VComponent<'a> {
pub name: &'static str,
pub static_props: bool,
pub props: Cell<*mut dyn AnyProps<'a>>,
pub placeholder: Cell<Option<ElementId>>,
pub scope: Cell<Option<ScopeId>>,
}
#[derive(Debug)]
pub struct VText<'a> {
pub id: Cell<ElementId>,
pub value: &'a str,
pub inner: bool,
}
#[derive(Debug)]
pub struct VFragment<'a> {
pub nodes: &'a [VNode<'a>],
pub inner: bool,
}
#[derive(Debug)]
pub enum TemplateAttribute<'a> {
Static {
@@ -163,32 +188,16 @@ impl<'a> AttributeValue<'a> {
cx: &'a ScopeState,
mut f: impl FnMut(UiEvent<T>) + 'a,
) -> AttributeValue<'a> {
println!("creating listener with type od id {:?}", TypeId::of::<T>());
let f = cx.bump().alloc(move |a: UiEvent<dyn Any>| {
println!(
"incoming: {:?}, desired {:?}",
a.data.type_id(),
std::any::TypeId::of::<T>()
);
let data = a.data.downcast::<T>().unwrap();
// println!("casting to type {:?}", TypeId::of::<T>());
f(UiEvent {
bubbles: a.bubbles,
data,
})
// println!("listener called");
// if let Some(event) = a.downcast::<T>() {
// println!("downcast success");
// f(event)
// }
}) as &mut dyn FnMut(UiEvent<dyn Any>);
AttributeValue::Listener(RefCell::new(f))
AttributeValue::Listener(RefCell::new(cx.bump().alloc(
move |event: UiEvent<dyn Any>| {
if let Ok(data) = event.data.downcast::<T>() {
f(UiEvent {
bubbles: event.bubbles,
data,
})
}
},
)))
}
}
-96
View File
@@ -1,101 +1,5 @@
use crate::innerlude::*;
pub struct FragmentProps<'a>(Element<'a>);
pub struct FragmentBuilder<'a, const BUILT: bool>(Element<'a>);
impl<'a> FragmentBuilder<'a, false> {
pub fn children(self, children: Element<'a>) -> FragmentBuilder<'a, true> {
FragmentBuilder(children)
}
}
impl<'a, const A: bool> FragmentBuilder<'a, A> {
pub fn build(self) -> FragmentProps<'a> {
FragmentProps(self.0)
}
}
/// Access the children elements passed into the component
///
/// This enables patterns where a component is passed children from its parent.
///
/// ## Details
///
/// Unlike React, Dioxus allows *only* lists of children to be passed from parent to child - not arbitrary functions
/// or classes. If you want to generate nodes instead of accepting them as a list, consider declaring a closure
/// on the props that takes Context.
///
/// If a parent passes children into a component, the child will always re-render when the parent re-renders. In other
/// words, a component cannot be automatically memoized if it borrows nodes from its parent, even if the component's
/// props are valid for the static lifetime.
///
/// ## Example
///
/// ```rust, ignore
/// fn App(cx: Scope) -> Element {
/// cx.render(rsx!{
/// CustomCard {
/// h1 {}
/// p {}
/// }
/// })
/// }
///
/// #[derive(PartialEq, Props)]
/// struct CardProps {
/// children: Element
/// }
///
/// fn CustomCard(cx: Scope<CardProps>) -> Element {
/// cx.render(rsx!{
/// div {
/// h1 {"Title card"}
/// {cx.props.children}
/// }
/// })
/// }
/// ```
impl<'a> Properties for FragmentProps<'a> {
type Builder = FragmentBuilder<'a, false>;
const IS_STATIC: bool = false;
fn builder() -> Self::Builder {
FragmentBuilder(None)
}
unsafe fn memoize(&self, _other: &Self) -> bool {
false
}
}
/// Create inline fragments using Component syntax.
///
/// ## Details
///
/// Fragments capture a series of children without rendering extra nodes.
///
/// Creating fragments explicitly with the Fragment component is particularly useful when rendering lists or tables and
/// a key is needed to identify each item.
///
/// ## Example
///
/// ```rust, ignore
/// rsx!{
/// Fragment { key: "abc" }
/// }
/// ```
///
/// ## Usage
///
/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
/// Try to avoid highly nested fragments if you can. Unlike React, there is no protection against infinitely nested fragments.
///
/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
///
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
#[allow(non_upper_case_globals, non_snake_case)]
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
// let i = cx.props.0.as_ref().map(|f| f.decouple());
// cx.render(LazyNodes::new(|f| f.fragment_from_iter(i)))
todo!("Fragment")
}
/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
/// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the
/// derive macro to implement the `Properties` trait automatically as guarantee that your memoization strategy is safe.
+6 -23
View File
@@ -1,6 +1,5 @@
use crate::{
any_props::AnyProps,
arena::ElementId,
bump_frame::BumpFrame,
factory::RenderReturn,
innerlude::{SuspenseId, SuspenseLeaf},
@@ -19,18 +18,16 @@ use std::{
impl VirtualDom {
pub(super) fn new_scope(&mut self, props: *mut dyn AnyProps<'static>) -> &mut ScopeState {
let parent = self.acquire_current_scope_raw();
let container = self.acquire_current_container();
let entry = self.scopes.vacant_entry();
let height = unsafe { parent.map(|f| (*f).height).unwrap_or(0) + 1 };
let id = ScopeId(entry.key());
entry.insert(ScopeState {
parent,
container,
id,
height,
props,
placeholder: None.into(),
placeholder: Default::default(),
node_arena_1: BumpFrame::new(50),
node_arena_2: BumpFrame::new(50),
spawned_tasks: Default::default(),
@@ -43,13 +40,6 @@ impl VirtualDom {
})
}
fn acquire_current_container(&self) -> ElementId {
self.element_stack
.last()
.copied()
.expect("Always have a container")
}
fn acquire_current_scope_raw(&mut self) -> Option<*mut ScopeState> {
self.scope_stack
.last()
@@ -65,15 +55,12 @@ impl VirtualDom {
}
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {
println!("run_scope: {:?}", scope_id);
let mut new_nodes = unsafe {
let scope = &mut self.scopes[scope_id.0];
println!("run_scope: scope: {:?}", scope.render_cnt.get());
scope.hook_idx.set(0);
// safety: due to how we traverse the tree, we know that the scope is not currently aliased
let props: &mut dyn AnyProps = mem::transmute(&mut *scope.props);
let props: &dyn AnyProps = mem::transmute(&*scope.props);
props.render(scope).extend_lifetime()
};
@@ -126,16 +113,12 @@ impl VirtualDom {
}
};
/*
todo: use proper mutability here
let scope = &self.scopes[scope_id.0];
right now we're aliasing the scope, which is not allowed
*/
// We write on top of the previous frame and then make it the current by pushing the generation forward
let frame = scope.previous_frame();
let scope = &mut self.scopes[scope_id.0];
let frame = scope.current_frame();
// set the head of the bump frame
// set the new head of the bump frame
let alloced = frame.bump.alloc(new_nodes);
frame.node.set(alloced);
+6 -2
View File
@@ -27,6 +27,11 @@ use std::{
/// component render.
pub type Scope<'a, T = ()> = &'a Scoped<'a, T>;
// 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.
pub struct Scoped<'a, T = ()> {
@@ -64,7 +69,6 @@ pub struct ScopeState {
pub(crate) node_arena_2: BumpFrame,
pub(crate) parent: Option<*mut ScopeState>,
pub(crate) container: ElementId,
pub(crate) id: ScopeId,
pub(crate) height: u32,
@@ -78,7 +82,7 @@ pub struct ScopeState {
pub(crate) tasks: Rc<Scheduler>,
pub(crate) spawned_tasks: HashSet<TaskId>,
pub(crate) props: *mut dyn AnyProps<'static>,
pub(crate) props: *const dyn AnyProps<'static>,
pub(crate) placeholder: Cell<Option<ElementId>>,
}
-2
View File
@@ -151,7 +151,6 @@ use std::{
pub struct VirtualDom {
pub(crate) templates: HashMap<TemplateId, Template<'static>>,
pub(crate) scopes: Slab<ScopeState>,
pub(crate) element_stack: Vec<ElementId>,
pub(crate) dirty_scopes: BTreeSet<DirtyScope>,
pub(crate) scheduler: Rc<Scheduler>,
@@ -236,7 +235,6 @@ impl VirtualDom {
scopes: Slab::default(),
elements: Default::default(),
scope_stack: Vec::new(),
element_stack: vec![ElementId(0)],
dirty_scopes: BTreeSet::new(),
collected_leaves: Vec::new(),
finished_fibers: Vec::new(),
+1 -1
View File
@@ -6,6 +6,6 @@ use std::rc::Rc;
pub fn use_atom_root(cx: &ScopeState) -> &Rc<AtomRoot> {
cx.use_hook(|| match cx.consume_context::<Rc<AtomRoot>>() {
Some(root) => root,
None => cx.provide_root_context(Rc::new(AtomRoot::new(cx.schedule_update_any()))),
None => panic!("No atom root found in context. Did you forget place an AtomRoot component at the top of your app?"),
})
}