polish: clean up the core crate
This commit is contained in:
1 parent
1e4a599d14
commit
e6c6bbdc1e
15 files changed
+760
-434
No files matched your search
@@ -105,11 +105,11 @@ use DomEdit::*;
|
|||||||
///
|
///
|
||||||
/// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's
|
/// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's
|
||||||
/// stack machines all the way down!
|
/// stack machines all the way down!
|
||||||
pub struct DiffState<'bump> {
|
pub(crate) struct DiffState<'bump> {
|
||||||
scopes: &'bump ScopeArena,
|
pub(crate) scopes: &'bump ScopeArena,
|
||||||
pub mutations: Mutations<'bump>,
|
pub(crate) mutations: Mutations<'bump>,
|
||||||
pub(crate) stack: DiffStack<'bump>,
|
pub(crate) stack: DiffStack<'bump>,
|
||||||
pub force_diff: bool,
|
pub(crate) force_diff: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'bump> DiffState<'bump> {
|
impl<'bump> DiffState<'bump> {
|
||||||
@@ -149,7 +149,7 @@ pub(crate) enum DiffInstruction<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum MountType<'a> {
|
pub(crate) enum MountType<'a> {
|
||||||
Absorb,
|
Absorb,
|
||||||
Append,
|
Append,
|
||||||
Replace { old: &'a VNode<'a> },
|
Replace { old: &'a VNode<'a> },
|
||||||
@@ -159,9 +159,9 @@ pub enum MountType<'a> {
|
|||||||
|
|
||||||
pub(crate) struct DiffStack<'bump> {
|
pub(crate) struct DiffStack<'bump> {
|
||||||
pub(crate) instructions: Vec<DiffInstruction<'bump>>,
|
pub(crate) instructions: Vec<DiffInstruction<'bump>>,
|
||||||
nodes_created_stack: SmallVec<[usize; 10]>,
|
pub(crate) nodes_created_stack: SmallVec<[usize; 10]>,
|
||||||
pub scope_stack: SmallVec<[ScopeId; 5]>,
|
pub(crate) scope_stack: SmallVec<[ScopeId; 5]>,
|
||||||
pub element_stack: SmallVec<[ElementId; 10]>,
|
pub(crate) element_stack: SmallVec<[ElementId; 10]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'bump> DiffStack<'bump> {
|
impl<'bump> DiffStack<'bump> {
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
/*
|
|
||||||
Navigating this crate:
|
|
||||||
- virtual_dom: the primary entrypoint for the crate
|
|
||||||
- scheduler: the core interior logic called by the [`VirtualDom`]
|
|
||||||
- nodes: the definition of VNodes, listeners, etc.
|
|
||||||
- diff: the stackmachine-based diffing algorithm
|
|
||||||
- hooks: foundational hooks that require crate-private APIs
|
|
||||||
- mutations: DomEdits/NodeRefs and internal API to create them
|
|
||||||
|
|
||||||
Some utilities
|
|
||||||
*/
|
|
||||||
pub(crate) mod component;
|
pub(crate) mod component;
|
||||||
pub(crate) mod diff;
|
pub(crate) mod diff;
|
||||||
pub(crate) mod lazynodes;
|
pub(crate) mod lazynodes;
|
||||||
@@ -23,7 +12,7 @@ pub(crate) mod virtual_dom;
|
|||||||
|
|
||||||
pub(crate) mod innerlude {
|
pub(crate) mod innerlude {
|
||||||
pub use crate::component::*;
|
pub use crate::component::*;
|
||||||
pub use crate::diff::*;
|
pub(crate) use crate::diff::*;
|
||||||
pub use crate::lazynodes::*;
|
pub use crate::lazynodes::*;
|
||||||
pub use crate::mutations::*;
|
pub use crate::mutations::*;
|
||||||
pub use crate::nodes::*;
|
pub use crate::nodes::*;
|
||||||
@@ -37,7 +26,7 @@ pub(crate) mod innerlude {
|
|||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
Attribute, Component, Context, DioxusElement, DomEdit, Element, ElementId, EventHandler,
|
Attribute, Component, Context, DioxusElement, DomEdit, Element, ElementId, EventHandler,
|
||||||
EventPriority, IntoVNode, LazyNodes, Listener, MountType, Mutations, NodeFactory, Properties,
|
EventPriority, IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties,
|
||||||
SchedulerMsg, ScopeId, UserEvent, VElement, VFragment, VNode, VirtualDom,
|
SchedulerMsg, ScopeId, UserEvent, VElement, VFragment, VNode, VirtualDom,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ pub struct Mutations<'a> {
|
|||||||
pub edits: Vec<DomEdit<'a>>,
|
pub edits: Vec<DomEdit<'a>>,
|
||||||
pub dirty_scopes: FxHashSet<ScopeId>,
|
pub dirty_scopes: FxHashSet<ScopeId>,
|
||||||
pub refs: Vec<NodeRefMutation<'a>>,
|
pub refs: Vec<NodeRefMutation<'a>>,
|
||||||
pub effects: Vec<&'a dyn FnMut()>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Mutations<'_> {
|
impl Debug for Mutations<'_> {
|
||||||
@@ -113,7 +112,6 @@ impl<'a> Mutations<'a> {
|
|||||||
edits: Vec::new(),
|
edits: Vec::new(),
|
||||||
refs: Vec::new(),
|
refs: Vec::new(),
|
||||||
dirty_scopes: Default::default(),
|
dirty_scopes: Default::default(),
|
||||||
effects: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+49
-50
@@ -7,6 +7,7 @@ use std::{
|
|||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
future::Future,
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,6 +33,14 @@ use bumpalo::{boxed::Box as BumpBox, Bump};
|
|||||||
/// ```
|
/// ```
|
||||||
pub type Context<'a> = &'a Scope;
|
pub type Context<'a> = &'a Scope;
|
||||||
|
|
||||||
|
/// A component's unique identifier.
|
||||||
|
///
|
||||||
|
/// `ScopeId` is a `usize` that is unique across the entire VirtualDOM and across time. ScopeIDs will never be reused
|
||||||
|
/// once a component has been unmounted.
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct ScopeId(pub usize);
|
||||||
|
|
||||||
/// Every component in Dioxus is represented by a `Scope`.
|
/// Every component in Dioxus is represented by a `Scope`.
|
||||||
///
|
///
|
||||||
/// Scopes contain the state for hooks, the component's props, and other lifecycle information.
|
/// Scopes contain the state for hooks, the component's props, and other lifecycle information.
|
||||||
@@ -44,7 +53,6 @@ pub type Context<'a> = &'a Scope;
|
|||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
pub(crate) parent_scope: Option<*mut Scope>,
|
pub(crate) parent_scope: Option<*mut Scope>,
|
||||||
|
|
||||||
// parent element I think?
|
|
||||||
pub(crate) container: ElementId,
|
pub(crate) container: ElementId,
|
||||||
|
|
||||||
pub(crate) our_arena_idx: ScopeId,
|
pub(crate) our_arena_idx: ScopeId,
|
||||||
@@ -75,17 +83,9 @@ pub struct Scope {
|
|||||||
pub struct SelfReferentialItems<'a> {
|
pub struct SelfReferentialItems<'a> {
|
||||||
pub(crate) listeners: Vec<&'a Listener<'a>>,
|
pub(crate) listeners: Vec<&'a Listener<'a>>,
|
||||||
pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
|
pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
|
||||||
pub(crate) tasks: Vec<BumpBox<'a, dyn Future<Output = ()>>>,
|
pub(crate) tasks: Vec<Pin<BumpBox<'a, dyn Future<Output = ()>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component's unique identifier.
|
|
||||||
///
|
|
||||||
/// `ScopeId` is a `usize` that is unique across the entire VirtualDOM and across time. ScopeIDs will never be reused
|
|
||||||
/// once a component has been unmounted.
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub struct ScopeId(pub usize);
|
|
||||||
|
|
||||||
// Public methods exposed to libraries and components
|
// Public methods exposed to libraries and components
|
||||||
impl Scope {
|
impl Scope {
|
||||||
/// Get the subtree ID that this scope belongs to.
|
/// Get the subtree ID that this scope belongs to.
|
||||||
@@ -227,11 +227,11 @@ impl Scope {
|
|||||||
let _ = self.sender.unbounded_send(SchedulerMsg::Immediate(id));
|
let _ = self.sender.unbounded_send(SchedulerMsg::Immediate(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`ScopeId`] of a mounted component.
|
/// Get the Root Node of this scope
|
||||||
///
|
pub fn root_node(&self) -> &VNode {
|
||||||
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
|
todo!("Portals have changed how we address nodes. Still fixing this, sorry.");
|
||||||
pub fn bump(&self) -> &Bump {
|
// let node = *self.wip_frame().nodes.borrow().get(0).unwrap();
|
||||||
&self.wip_frame().bump
|
// unsafe { std::mem::transmute(&*node) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method enables the ability to expose state to children further down the VirtualDOM Tree.
|
/// This method enables the ability to expose state to children further down the VirtualDOM Tree.
|
||||||
@@ -287,15 +287,10 @@ impl Scope {
|
|||||||
|
|
||||||
/// Pushes the future onto the poll queue to be polled after the component renders.
|
/// Pushes the future onto the poll queue to be polled after the component renders.
|
||||||
///
|
///
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// The future is forcibly dropped if the component is not ready by the next render
|
/// The future is forcibly dropped if the component is not ready by the next render
|
||||||
pub fn push_task<'src, F: Future<Output = ()>>(
|
pub fn push_task<'src, F>(&'src self, fut: impl FnOnce() -> F + 'src) -> usize
|
||||||
&'src self,
|
|
||||||
fut: impl FnOnce() -> F + 'src,
|
|
||||||
) -> usize
|
|
||||||
where
|
where
|
||||||
|
F: Future<Output = ()>,
|
||||||
F::Output: 'src,
|
F::Output: 'src,
|
||||||
F: 'src,
|
F: 'src,
|
||||||
{
|
{
|
||||||
@@ -303,18 +298,20 @@ impl Scope {
|
|||||||
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// allocate the future
|
|
||||||
let fut = fut();
|
|
||||||
let fut: &mut dyn Future<Output = ()> = self.bump().alloc(fut);
|
|
||||||
|
|
||||||
// wrap it in a type that will actually drop the contents
|
// wrap it in a type that will actually drop the contents
|
||||||
|
//
|
||||||
|
// Safety: we just made the pointer above and will promise not to alias it!
|
||||||
|
// The main reason we do this through from_raw is because Bumpalo's box does
|
||||||
|
// not support unsized coercion
|
||||||
|
let fut: &mut dyn Future<Output = ()> = self.bump().alloc(fut());
|
||||||
let boxed_fut: BumpBox<dyn Future<Output = ()>> = unsafe { BumpBox::from_raw(fut) };
|
let boxed_fut: BumpBox<dyn Future<Output = ()>> = unsafe { BumpBox::from_raw(fut) };
|
||||||
|
let pinned_fut: Pin<BumpBox<_>> = boxed_fut.into();
|
||||||
|
|
||||||
// erase the 'src lifetime for self-referential storage
|
// erase the 'src lifetime for self-referential storage
|
||||||
let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
|
let self_ref_fut = unsafe { std::mem::transmute(pinned_fut) };
|
||||||
|
|
||||||
|
// Push the future into the tasks
|
||||||
let mut items = self.items.borrow_mut();
|
let mut items = self.items.borrow_mut();
|
||||||
|
|
||||||
items.tasks.push(self_ref_fut);
|
items.tasks.push(self_ref_fut);
|
||||||
items.tasks.len() - 1
|
items.tasks.len() - 1
|
||||||
}
|
}
|
||||||
@@ -335,22 +332,19 @@ impl Scope {
|
|||||||
/// }
|
/// }
|
||||||
///```
|
///```
|
||||||
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VPortal> {
|
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VPortal> {
|
||||||
let frame = self.wip_frame();
|
let bump = &self.wip_frame().bump;
|
||||||
let bump = &frame.bump;
|
|
||||||
let factory = NodeFactory { bump };
|
|
||||||
let node = rsx.map(|f| f.call(factory))?;
|
|
||||||
let node = bump.alloc(node);
|
|
||||||
|
|
||||||
let node_ptr = node as *mut _;
|
let owned_node: VNode<'src> = rsx.map(|f| f.call(NodeFactory { bump }))?;
|
||||||
let node_ptr = unsafe { std::mem::transmute(node_ptr) };
|
let alloced_vnode: &'src mut VNode<'src> = bump.alloc(owned_node);
|
||||||
|
let node_ptr: *mut VNode<'src> = alloced_vnode as *mut _;
|
||||||
|
|
||||||
let link = VPortal {
|
let node: *mut VNode<'static> = unsafe { std::mem::transmute(node_ptr) };
|
||||||
|
|
||||||
|
Some(VPortal {
|
||||||
scope_id: Cell::new(Some(self.our_arena_idx)),
|
scope_id: Cell::new(Some(self.our_arena_idx)),
|
||||||
link_idx: Cell::new(0),
|
link_idx: Cell::new(0),
|
||||||
node: node_ptr,
|
node,
|
||||||
};
|
})
|
||||||
|
|
||||||
Some(link)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a value between renders
|
/// Store a value between renders
|
||||||
@@ -380,12 +374,12 @@ impl Scope {
|
|||||||
runner: impl FnOnce(&'src mut State) -> Output,
|
runner: impl FnOnce(&'src mut State) -> Output,
|
||||||
) -> Output {
|
) -> Output {
|
||||||
let mut vals = self.hook_vals.borrow_mut();
|
let mut vals = self.hook_vals.borrow_mut();
|
||||||
|
|
||||||
let hook_len = vals.len();
|
let hook_len = vals.len();
|
||||||
let cur_idx = self.hook_idx.get();
|
let cur_idx = self.hook_idx.get();
|
||||||
|
|
||||||
if cur_idx >= hook_len {
|
if cur_idx >= hook_len {
|
||||||
let val = self.hook_arena.alloc(initializer(hook_len));
|
vals.push(self.hook_arena.alloc(initializer(hook_len)));
|
||||||
vals.push(val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = vals
|
let state = vals
|
||||||
@@ -416,6 +410,7 @@ impl Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mutable access to the "work in progress frame" - used to clear it
|
||||||
pub(crate) fn wip_frame_mut(&mut self) -> &mut BumpFrame {
|
pub(crate) fn wip_frame_mut(&mut self) -> &mut BumpFrame {
|
||||||
match self.generation.get() & 1 == 0 {
|
match self.generation.get() & 1 == 0 {
|
||||||
true => &mut self.frames[0],
|
true => &mut self.frames[0],
|
||||||
@@ -423,6 +418,7 @@ impl Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access to the frame where finalized nodes existed
|
||||||
pub(crate) fn fin_frame(&self) -> &BumpFrame {
|
pub(crate) fn fin_frame(&self) -> &BumpFrame {
|
||||||
match self.generation.get() & 1 == 1 {
|
match self.generation.get() & 1 == 1 {
|
||||||
true => &self.frames[0],
|
true => &self.frames[0],
|
||||||
@@ -433,20 +429,23 @@ impl Scope {
|
|||||||
/// Reset this component's frame
|
/// Reset this component's frame
|
||||||
///
|
///
|
||||||
/// # Safety:
|
/// # Safety:
|
||||||
|
///
|
||||||
/// This method breaks every reference of VNodes in the current frame.
|
/// This method breaks every reference of VNodes in the current frame.
|
||||||
|
///
|
||||||
|
/// Calling reset itself is not usually a big deal, but we consider it important
|
||||||
|
/// due to the complex safety guarantees we need to uphold.
|
||||||
pub(crate) unsafe fn reset_wip_frame(&mut self) {
|
pub(crate) unsafe fn reset_wip_frame(&mut self) {
|
||||||
// todo: unsafecell or something
|
self.wip_frame_mut().bump.reset();
|
||||||
let bump = self.wip_frame_mut();
|
|
||||||
bump.bump.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cycle to the next generation
|
||||||
pub(crate) fn cycle_frame(&self) {
|
pub(crate) fn cycle_frame(&self) {
|
||||||
self.generation.set(self.generation.get() + 1);
|
self.generation.set(self.generation.get() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_node(&self) -> &VNode {
|
/// Get the [`Bump`] of the WIP frame.
|
||||||
let node = *self.wip_frame().nodes.borrow().get(0).unwrap();
|
pub(crate) fn bump(&self) -> &Bump {
|
||||||
unsafe { std::mem::transmute(&*node) }
|
&self.wip_frame().bump
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +454,7 @@ pub(crate) struct BumpFrame {
|
|||||||
pub nodes: RefCell<Vec<*const VNode<'static>>>,
|
pub nodes: RefCell<Vec<*const VNode<'static>>>,
|
||||||
}
|
}
|
||||||
impl BumpFrame {
|
impl BumpFrame {
|
||||||
pub fn new(capacity: usize) -> Self {
|
pub(crate) fn new(capacity: usize) -> Self {
|
||||||
let bump = Bump::with_capacity(capacity);
|
let bump = Bump::with_capacity(capacity);
|
||||||
|
|
||||||
let node = &*bump.alloc(VText {
|
let node = &*bump.alloc(VText {
|
||||||
@@ -468,7 +467,7 @@ impl BumpFrame {
|
|||||||
Self { bump, nodes }
|
Self { bump, nodes }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign_nodelink(&self, node: &VPortal) {
|
pub(crate) fn assign_nodelink(&self, node: &VPortal) {
|
||||||
let mut nodes = self.nodes.borrow_mut();
|
let mut nodes = self.nodes.borrow_mut();
|
||||||
|
|
||||||
let len = nodes.len();
|
let len = nodes.len();
|
||||||
|
|||||||
@@ -92,15 +92,8 @@ impl ScopeArena {
|
|||||||
let new_scope_id = ScopeId(self.scope_counter.get());
|
let new_scope_id = ScopeId(self.scope_counter.get());
|
||||||
self.scope_counter.set(self.scope_counter.get() + 1);
|
self.scope_counter.set(self.scope_counter.get() + 1);
|
||||||
|
|
||||||
// log::debug!("new scope {:?} with parent {:?}", new_scope_id, container);
|
|
||||||
|
|
||||||
if let Some(old_scope) = self.free_scopes.borrow_mut().pop() {
|
if let Some(old_scope) = self.free_scopes.borrow_mut().pop() {
|
||||||
let scope = unsafe { &mut *old_scope };
|
let scope = unsafe { &mut *old_scope };
|
||||||
// log::debug!(
|
|
||||||
// "reusing scope {:?} as {:?}",
|
|
||||||
// scope.our_arena_idx,
|
|
||||||
// new_scope_id
|
|
||||||
// );
|
|
||||||
|
|
||||||
scope.caller = caller;
|
scope.caller = caller;
|
||||||
scope.parent_scope = parent_scope;
|
scope.parent_scope = parent_scope;
|
||||||
|
|||||||
@@ -183,6 +183,11 @@ impl VirtualDom {
|
|||||||
///
|
///
|
||||||
/// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
|
/// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
|
||||||
/// VirtualDom to be created just to retrieve its channel receiver.
|
/// VirtualDom to be created just to retrieve its channel receiver.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let (sender, receiver) = futures_channel::mpsc::unbounded();
|
||||||
|
/// let dom = VirtualDom::new_with_scheduler(Example, (), sender, receiver);
|
||||||
|
/// ```
|
||||||
pub fn new_with_props_and_scheduler<P: 'static>(
|
pub fn new_with_props_and_scheduler<P: 'static>(
|
||||||
root: Component<P>,
|
root: Component<P>,
|
||||||
root_props: P,
|
root_props: P,
|
||||||
@@ -235,27 +240,55 @@ impl VirtualDom {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust, ignore
|
/// ```rust, ignore
|
||||||
///
|
/// let dom = VirtualDom::new(App);
|
||||||
///
|
/// let sender = dom.get_scheduler_channel();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
|
pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
|
||||||
self.sender.clone()
|
self.sender.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a new message to the scheduler queue directly.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// This method makes it possible to send messages to the scheduler from outside the VirtualDom without having to
|
||||||
|
/// call `get_schedule_channel` and then `send`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust, ignore
|
||||||
|
/// let dom = VirtualDom::new(App);
|
||||||
|
/// dom.insert_scheduler_message(SchedulerMsg::Immediate(ScopeId(0)));
|
||||||
|
/// ```
|
||||||
|
pub fn insert_scheduler_message(&self, msg: SchedulerMsg) {
|
||||||
|
self.sender.unbounded_send(msg).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if the [`VirtualDom`] has any pending updates or work to be done.
|
/// Check if the [`VirtualDom`] has any pending updates or work to be done.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust, ignore
|
/// ```rust, ignore
|
||||||
|
/// let dom = VirtualDom::new(App);
|
||||||
///
|
///
|
||||||
///
|
/// // the dom is "dirty" when it is started and must be rebuilt to get the first render
|
||||||
|
/// assert!(dom.has_any_work());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn has_any_work(&self) -> bool {
|
pub fn has_work(&self) -> bool {
|
||||||
!(self.dirty_scopes.is_empty() && self.pending_messages.is_empty())
|
!(self.dirty_scopes.is_empty() && self.pending_messages.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits for the scheduler to have work
|
/// Wait for the scheduler to have any work.
|
||||||
|
///
|
||||||
|
/// This method polls the internal future queue *and* the scheduler channel.
|
||||||
|
/// To add work to the VirtualDom, insert a message via the scheduler channel.
|
||||||
|
///
|
||||||
/// This lets us poll async tasks during idle periods without blocking the main thread.
|
/// This lets us poll async tasks during idle periods without blocking the main thread.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust, ignore
|
||||||
|
/// let dom = VirtualDom::new(App);
|
||||||
|
/// let sender = dom.get_scheduler_channel();
|
||||||
|
/// ```
|
||||||
pub async fn wait_for_work(&mut self) {
|
pub async fn wait_for_work(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
if !self.dirty_scopes.is_empty() && self.pending_messages.is_empty() {
|
if !self.dirty_scopes.is_empty() && self.pending_messages.is_empty() {
|
||||||
@@ -390,9 +423,10 @@ impl VirtualDom {
|
|||||||
|
|
||||||
committed_mutations.push(mutations);
|
committed_mutations.push(mutations);
|
||||||
} else {
|
} else {
|
||||||
log::debug!("Could not finish work in time");
|
|
||||||
|
|
||||||
// leave the work in an incomplete state
|
// leave the work in an incomplete state
|
||||||
|
//
|
||||||
|
// todo: we should store the edits and re-apply them later
|
||||||
|
// for now, we just dump the work completely (threadsafe)
|
||||||
return committed_mutations;
|
return committed_mutations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,7 +434,7 @@ impl VirtualDom {
|
|||||||
committed_mutations
|
committed_mutations
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch
|
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch.
|
||||||
///
|
///
|
||||||
/// The diff machine expects the RealDom's stack to be the root of the application.
|
/// The diff machine expects the RealDom's stack to be the root of the application.
|
||||||
///
|
///
|
||||||
@@ -475,6 +509,15 @@ impl VirtualDom {
|
|||||||
/// Renders an `rsx` call into the Base Scope's allocator.
|
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||||
///
|
///
|
||||||
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// fn Base(cx: Context, props: &()) -> Element {
|
||||||
|
/// rsx!(cx, div {})
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let dom = VirtualDom::new(Base);
|
||||||
|
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||||
|
/// ```
|
||||||
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
||||||
let scope = self.scopes.get_scope(&self.base_scope).unwrap();
|
let scope = self.scopes.get_scope(&self.base_scope).unwrap();
|
||||||
let frame = scope.wip_frame();
|
let frame = scope.wip_frame();
|
||||||
@@ -485,7 +528,16 @@ impl VirtualDom {
|
|||||||
|
|
||||||
/// Renders an `rsx` call into the Base Scope's allocator.
|
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||||
///
|
///
|
||||||
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// fn Base(cx: Context, props: &()) -> Element {
|
||||||
|
/// rsx!(cx, div {})
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let dom = VirtualDom::new(Base);
|
||||||
|
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||||
|
/// ```
|
||||||
pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
|
pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
|
||||||
let mut machine = DiffState::new(&self.scopes);
|
let mut machine = DiffState::new(&self.scopes);
|
||||||
machine.stack.push(DiffInstruction::Diff { new, old });
|
machine.stack.push(DiffInstruction::Diff { new, old });
|
||||||
@@ -498,6 +550,16 @@ impl VirtualDom {
|
|||||||
/// Renders an `rsx` call into the Base Scope's allocator.
|
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||||
///
|
///
|
||||||
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// fn Base(cx: Context, props: &()) -> Element {
|
||||||
|
/// rsx!(cx, div {})
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let dom = VirtualDom::new(Base);
|
||||||
|
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||||
|
/// ```
|
||||||
pub fn create_vnodes<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
|
pub fn create_vnodes<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
|
||||||
let nodes = self.render_vnodes(left);
|
let nodes = self.render_vnodes(left);
|
||||||
let mut machine = DiffState::new(&self.scopes);
|
let mut machine = DiffState::new(&self.scopes);
|
||||||
@@ -507,9 +569,19 @@ impl VirtualDom {
|
|||||||
machine.mutations
|
machine.mutations
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders an `rsx` call into the Base Scope's allocator.
|
/// Renders an `rsx` call into the Base Scopes's arena.
|
||||||
///
|
///
|
||||||
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
/// Useful when needing to diff two rsx! calls from outside the VirtualDom, such as in a test.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// fn Base(cx: Context, props: &()) -> Element {
|
||||||
|
/// rsx!(cx, div {})
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let dom = VirtualDom::new(Base);
|
||||||
|
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||||
|
/// ```
|
||||||
pub fn diff_lazynodes<'a>(
|
pub fn diff_lazynodes<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
left: Option<LazyNodes<'a, '_>>,
|
left: Option<LazyNodes<'a, '_>>,
|
||||||
@@ -666,14 +738,7 @@ impl<'a> Future for PollTasks<'a> {
|
|||||||
|
|
||||||
// really this should just be retain_mut but that doesn't exist yet
|
// really this should just be retain_mut but that doesn't exist yet
|
||||||
while let Some(mut task) = items.tasks.pop() {
|
while let Some(mut task) = items.tasks.pop() {
|
||||||
// todo: does this make sense?
|
if task.as_mut().poll(cx).is_ready() {
|
||||||
// I don't usually write futures by hand
|
|
||||||
// I think the futures neeed to be pinned using bumpbox or something
|
|
||||||
// right now, they're bump allocated so this shouldn't matter anyway - they're not going to move
|
|
||||||
let task_mut = task.as_mut();
|
|
||||||
let pinned = unsafe { Pin::new_unchecked(task_mut) };
|
|
||||||
|
|
||||||
if pinned.poll(cx).is_ready() {
|
|
||||||
all_pending = false
|
all_pending = false
|
||||||
} else {
|
} else {
|
||||||
unfinished_tasks.push(task);
|
unfinished_tasks.push(task);
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ fn events_generate() {
|
|||||||
|
|
||||||
let mut dom = VirtualDom::new(App);
|
let mut dom = VirtualDom::new(App);
|
||||||
let mut channel = dom.get_scheduler_channel();
|
let mut channel = dom.get_scheduler_channel();
|
||||||
assert!(dom.has_any_work());
|
assert!(dom.has_work());
|
||||||
|
|
||||||
let edits = dom.rebuild();
|
let edits = dom.rebuild();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
mod usestate;
|
mod usestate;
|
||||||
pub use usestate::{use_state, AsyncUseState, UseState};
|
pub use usestate::{use_state, AsyncUseState, UseState};
|
||||||
|
|
||||||
|
mod usestate2;
|
||||||
|
// pub use usestate2::use_state2;
|
||||||
|
|
||||||
mod useref;
|
mod useref;
|
||||||
pub use useref::*;
|
pub use useref::*;
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,15 @@ pub struct CoroutineHandle<'a> {
|
|||||||
cx: Context<'a>,
|
cx: Context<'a>,
|
||||||
inner: &'a State,
|
inner: &'a State,
|
||||||
}
|
}
|
||||||
|
impl Clone for CoroutineHandle<'_> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
CoroutineHandle {
|
||||||
|
cx: self.cx,
|
||||||
|
inner: self.inner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Copy for CoroutineHandle<'_> {}
|
||||||
|
|
||||||
impl<'a> CoroutineHandle<'a> {
|
impl<'a> CoroutineHandle<'a> {
|
||||||
pub fn start(&self) {
|
pub fn start(&self) {
|
||||||
|
|||||||
@@ -54,18 +54,26 @@ pub fn use_state<'a, T: 'static>(
|
|||||||
initial_state_fn: impl FnOnce() -> T,
|
initial_state_fn: impl FnOnce() -> T,
|
||||||
) -> UseState<'a, T> {
|
) -> UseState<'a, T> {
|
||||||
cx.use_hook(
|
cx.use_hook(
|
||||||
move |_| UseStateInner {
|
move |_| {
|
||||||
current_val: initial_state_fn(),
|
let first_val = initial_state_fn();
|
||||||
update_callback: cx.schedule_update(),
|
UseStateInner {
|
||||||
wip: Rc::new(RefCell::new(None)),
|
current_val: Rc::new(first_val),
|
||||||
update_scheuled: Cell::new(false),
|
update_callback: cx.schedule_update(),
|
||||||
|
wip: Rc::new(RefCell::new(None)),
|
||||||
|
update_scheuled: Cell::new(false),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
move |hook| {
|
move |hook| {
|
||||||
hook.update_scheuled.set(false);
|
hook.update_scheuled.set(false);
|
||||||
|
|
||||||
let mut new_val = hook.wip.borrow_mut();
|
let mut new_val = hook.wip.borrow_mut();
|
||||||
if new_val.is_some() {
|
if new_val.is_some() {
|
||||||
hook.current_val = new_val.take().unwrap();
|
// if there's only one reference (weak or otherwise), we can just swap the values
|
||||||
|
if let Some(val) = Rc::get_mut(&mut hook.current_val) {
|
||||||
|
*val = new_val.take().unwrap();
|
||||||
|
} else {
|
||||||
|
hook.current_val = Rc::new(new_val.take().unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UseState { inner: &*hook }
|
UseState { inner: &*hook }
|
||||||
@@ -73,7 +81,7 @@ pub fn use_state<'a, T: 'static>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
struct UseStateInner<T: 'static> {
|
struct UseStateInner<T: 'static> {
|
||||||
current_val: T,
|
current_val: Rc<T>,
|
||||||
update_scheuled: Cell<bool>,
|
update_scheuled: Cell<bool>,
|
||||||
update_callback: Rc<dyn Fn()>,
|
update_callback: Rc<dyn Fn()>,
|
||||||
wip: Rc<RefCell<Option<T>>>,
|
wip: Rc<RefCell<Option<T>>>,
|
||||||
@@ -115,6 +123,10 @@ impl<'a, T: 'static> UseState<'a, T> {
|
|||||||
&self.inner.current_val
|
&self.inner.current_val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rc(&self) -> &'a Rc<T> {
|
||||||
|
&self.inner.current_val
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the current status of the work-in-progress data
|
/// Get the current status of the work-in-progress data
|
||||||
pub fn get_wip(&self) -> Ref<Option<T>> {
|
pub fn get_wip(&self) -> Ref<Option<T>> {
|
||||||
self.inner.wip.borrow()
|
self.inner.wip.borrow()
|
||||||
@@ -140,6 +152,7 @@ impl<'a, T: 'static> UseState<'a, T> {
|
|||||||
AsyncUseState {
|
AsyncUseState {
|
||||||
re_render: self.inner.update_callback.clone(),
|
re_render: self.inner.update_callback.clone(),
|
||||||
wip: self.inner.wip.clone(),
|
wip: self.inner.wip.clone(),
|
||||||
|
inner: self.inner.current_val.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,14 +175,14 @@ impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
|
|||||||
// "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
|
// "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
|
||||||
RefMut::map(self.inner.wip.borrow_mut(), |slot| {
|
RefMut::map(self.inner.wip.borrow_mut(), |slot| {
|
||||||
if slot.is_none() {
|
if slot.is_none() {
|
||||||
*slot = Some(self.inner.current_val.to_owned());
|
*slot = Some(self.inner.current_val.as_ref().to_owned());
|
||||||
}
|
}
|
||||||
slot.as_mut().unwrap()
|
slot.as_mut().unwrap()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner(self) -> T {
|
pub fn inner(self) -> T {
|
||||||
self.inner.current_val.to_owned()
|
self.inner.current_val.as_ref().to_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +269,7 @@ impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
|
|||||||
|
|
||||||
/// A less ergonmic but still capable form of use_state that's valid for `static lifetime
|
/// A less ergonmic but still capable form of use_state that's valid for `static lifetime
|
||||||
pub struct AsyncUseState<T: 'static> {
|
pub struct AsyncUseState<T: 'static> {
|
||||||
|
inner: Rc<T>,
|
||||||
re_render: Rc<dyn Fn()>,
|
re_render: Rc<dyn Fn()>,
|
||||||
wip: Rc<RefCell<Option<T>>>,
|
wip: Rc<RefCell<Option<T>>>,
|
||||||
}
|
}
|
||||||
@@ -278,4 +292,11 @@ impl<T> AsyncUseState<T> {
|
|||||||
(self.re_render)();
|
(self.re_render)();
|
||||||
*self.wip.borrow_mut() = Some(val);
|
*self.wip.borrow_mut() = Some(val);
|
||||||
}
|
}
|
||||||
|
pub fn get(&self) -> &T {
|
||||||
|
self.inner.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rc(&self) -> &Rc<T> {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
use dioxus_core::prelude::Context;
|
||||||
|
use std::{
|
||||||
|
borrow::{Borrow, BorrowMut},
|
||||||
|
cell::{Cell, Ref, RefCell, RefMut},
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
ops::Not,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
// /// Store state between component renders!
|
||||||
|
// ///
|
||||||
|
// /// ## Dioxus equivalent of UseStateInner2, designed for Rust
|
||||||
|
// ///
|
||||||
|
// /// The Dioxus version of `UseStateInner2` is the "king daddy" of state management. It allows you to ergonomically store and
|
||||||
|
// /// modify state between component renders. When the state is updated, the component will re-render.
|
||||||
|
// ///
|
||||||
|
// /// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
|
||||||
|
// ///
|
||||||
|
// /// [`use_state`] exposes a few helper methods to modify the underlying state:
|
||||||
|
// /// - `.set(new)` allows you to override the "work in progress" value with a new value
|
||||||
|
// /// - `.get_mut()` allows you to modify the WIP value
|
||||||
|
// /// - `.get_wip()` allows you to access the WIP value
|
||||||
|
// /// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required)
|
||||||
|
// ///
|
||||||
|
// /// Additionally, a ton of std::ops traits are implemented for the `UseStateInner2` wrapper, meaning any mutative type operations
|
||||||
|
// /// will automatically be called on the WIP value.
|
||||||
|
// ///
|
||||||
|
// /// ## Combinators
|
||||||
|
// ///
|
||||||
|
// /// On top of the methods to set/get state, `use_state` also supports fancy combinators to extend its functionality:
|
||||||
|
// /// - `.classic()` and `.split()` convert the hook into the classic React-style hook
|
||||||
|
// /// ```rust
|
||||||
|
// /// let (state, set_state) = use_state(cx, || 10).split()
|
||||||
|
// /// ```
|
||||||
|
// ///
|
||||||
|
// ///
|
||||||
|
// /// Usage:
|
||||||
|
// /// ```ignore
|
||||||
|
// /// const Example: FC<()> = |cx, props|{
|
||||||
|
// /// let counter = use_state(cx, || 0);
|
||||||
|
// /// let increment = |_| counter += 1;
|
||||||
|
// /// let decrement = |_| counter += 1;
|
||||||
|
// ///
|
||||||
|
// /// html! {
|
||||||
|
// /// <div>
|
||||||
|
// /// <h1>"Counter: {counter}" </h1>
|
||||||
|
// /// <button onclick={increment}> "Increment" </button>
|
||||||
|
// /// <button onclick={decrement}> "Decrement" </button>
|
||||||
|
// /// </div>
|
||||||
|
// /// }
|
||||||
|
// /// }
|
||||||
|
// /// ```
|
||||||
|
// pub fn use_state2<'a, T: 'static>(
|
||||||
|
// cx: Context<'a>,
|
||||||
|
// initial_state_fn: impl FnOnce() -> T,
|
||||||
|
// ) -> &'a UseState2<T> {
|
||||||
|
// cx.use_hook(
|
||||||
|
// move |_| {
|
||||||
|
// UseState2(Rc::new(UseStateInner2 {
|
||||||
|
// current_val: initial_state_fn(),
|
||||||
|
// update_callback: cx.schedule_update(),
|
||||||
|
// wip: None,
|
||||||
|
// update_scheuled: Cell::new(false),
|
||||||
|
// }))
|
||||||
|
// },
|
||||||
|
// move |hook: &mut UseState2<T>| {
|
||||||
|
// {
|
||||||
|
// let r = hook.0.as_ref();
|
||||||
|
// let mut state = r.borrow_mut();
|
||||||
|
// state.update_scheuled.set(false);
|
||||||
|
// if state.wip.is_some() {
|
||||||
|
// state.current_val = state.wip.take().unwrap();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// &*hook
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct UseState2<T: 'static>(Rc<UseStateInner2<T>>);
|
||||||
|
|
||||||
|
// impl<T> ToOwned for UseState2<T> {
|
||||||
|
// type Owned = UseState2<T>;
|
||||||
|
// fn to_owned(&self) -> Self::Owned {
|
||||||
|
// UseState2(self.0.clone())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct UseStateInner2<T: 'static> {
|
||||||
|
// current_val: T,
|
||||||
|
// update_scheuled: Cell<bool>,
|
||||||
|
// update_callback: Rc<dyn Fn()>,
|
||||||
|
// wip: Option<T>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<T: Debug> Debug for UseStateInner2<T> {
|
||||||
|
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// write!(f, "{:?}", self.current_val)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<T> UseState2<T> {
|
||||||
|
// /// Tell the Dioxus Scheduler that we need to be processed
|
||||||
|
// pub fn needs_update(&self) {
|
||||||
|
// if !self.update_scheuled.get() {
|
||||||
|
// self.update_scheuled.set(true);
|
||||||
|
// (self.update_callback)();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn set(&mut self, new_val: T) -> Option<T> {
|
||||||
|
// self.needs_update();
|
||||||
|
// ip.wip.replace(new_val)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn get(&self) -> &T {
|
||||||
|
// &self.current_val
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // impl<T: 'static + ToOwned<Owned = T>> UseState2<T> {
|
||||||
|
// // /// Gain mutable access to the new value. This method is only available when the value is a `ToOwned` type.
|
||||||
|
// // ///
|
||||||
|
// // /// Mutable access is derived by calling "ToOwned" (IE cloning) on the current value.
|
||||||
|
// // ///
|
||||||
|
// // /// To get a reference to the current value, use `.get()`
|
||||||
|
// // pub fn modify(&self) -> RefMut<T> {
|
||||||
|
// // // make sure we get processed
|
||||||
|
// // self.0.needs_update();
|
||||||
|
|
||||||
|
// // // Bring out the new value, cloning if it we need to
|
||||||
|
// // // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
|
||||||
|
// // RefMut::map(self.wip.borrow_mut(), |slot| {
|
||||||
|
// // if slot.is_none() {
|
||||||
|
// // *slot = Some(self.current_val.to_owned());
|
||||||
|
// // }
|
||||||
|
// // slot.as_mut().unwrap()
|
||||||
|
// // })
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // pub fn inner(self) -> T {
|
||||||
|
// // self.current_val.to_owned()
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// impl<T> std::ops::Deref for UseStateInner2<T> {
|
||||||
|
// type Target = T;
|
||||||
|
|
||||||
|
// fn deref(&self) -> &Self::Target {
|
||||||
|
// self.get()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||||
|
|
||||||
|
// use crate::UseState;
|
||||||
|
|
||||||
|
// impl<T: Copy + Add<T, Output = T>> Add<T> for UseStateInner2<T> {
|
||||||
|
// type Output = T;
|
||||||
|
|
||||||
|
// fn add(self, rhs: T) -> Self::Output {
|
||||||
|
// self.current_val.add(rhs)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<T: Copy + Add<T, Output = T>> AddAssign<T> for UseStateInner2<T> {
|
||||||
|
// fn add_assign(&mut self, rhs: T) {
|
||||||
|
// self.set(self.current_val.add(rhs));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<T: Copy + Sub<T, Output = T>> Sub<T> for UseStateInner2<T> {
|
||||||
|
// type Output = T;
|
||||||
|
|
||||||
|
// fn sub(self, rhs: T) -> Self::Output {
|
||||||
|
// self.current_val.sub(rhs)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<T: Copy + Sub<T, Output = T>> SubAssign<T> for UseStateInner2<T> {
|
||||||
|
// fn sub_assign(&mut self, rhs: T) {
|
||||||
|
// self.set(self.current_val.sub(rhs));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// MUL
|
||||||
|
// impl<T: Copy + Mul<T, Output = T>> Mul<T> for UseStateInner2<T> {
|
||||||
|
// type Output = T;
|
||||||
|
|
||||||
|
// fn mul(self, rhs: T) -> Self::Output {
|
||||||
|
// self.current_val.mul(rhs)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<T: Copy + Mul<T, Output = T>> MulAssign<T> for UseStateInner2<T> {
|
||||||
|
// fn mul_assign(&mut self, rhs: T) {
|
||||||
|
// self.set(self.current_val.mul(rhs));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// /// DIV
|
||||||
|
// impl<T: Copy + Div<T, Output = T>> Div<T> for UseStateInner2<T> {
|
||||||
|
// type Output = T;
|
||||||
|
|
||||||
|
// fn div(self, rhs: T) -> Self::Output {
|
||||||
|
// self.current_val.div(rhs)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<T: Copy + Div<T, Output = T>> DivAssign<T> for UseStateInner2<T> {
|
||||||
|
// fn div_assign(&mut self, rhs: T) {
|
||||||
|
// self.set(self.current_val.div(rhs));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<V, T: PartialEq<V>> PartialEq<V> for UseStateInner2<T> {
|
||||||
|
// fn eq(&self, other: &V) -> bool {
|
||||||
|
// self.get() == other
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<O, T: Not<Output = O> + Copy> Not for UseStateInner2<T> {
|
||||||
|
// type Output = O;
|
||||||
|
|
||||||
|
// fn not(self) -> Self::Output {
|
||||||
|
// !*self.get()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // enable displaty for the handle
|
||||||
|
// impl<T: 'static + Display> std::fmt::Display for UseStateInner2<T> {
|
||||||
|
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// write!(f, "{}", self.current_val)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -22,3 +22,28 @@ async fn main() -> tide::Result<()> {
|
|||||||
|
|
||||||
Dioxus LiveView runs your Dioxus apps on the server
|
Dioxus LiveView runs your Dioxus apps on the server
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use soyuz::prelude::*;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let mut app = soyuz::new();
|
||||||
|
app.at("/app").get(websocket(handler));
|
||||||
|
app.listen("127.0.0.1:8080").await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn order_shoes(mut req: WebsocketRequest) -> Response {
|
||||||
|
let stream = req.upgrade();
|
||||||
|
dioxus::liveview::launch(App, stream).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn App(cx: Context, props: &()) -> Element {
|
||||||
|
let mut count = use_state(cx, || 0);
|
||||||
|
cx.render(rsx!(
|
||||||
|
button { onclick: move |_| count += 1, "Incr" }
|
||||||
|
button { onclick: move |_| count -= 1, "Decr" }
|
||||||
|
))
|
||||||
|
}
|
||||||
|
```
|
||||||
+298
-311
@@ -1,311 +1,298 @@
|
|||||||
/// Wasm-bindgen has a performance option to intern commonly used phrases
|
pub static BUILTIN_INTERNED_STRINGS: &[&'static str] = &[
|
||||||
/// This saves the decoding cost, making the interaction of Rust<->JS more performant.
|
// Important tags to dioxus
|
||||||
/// We intern all the HTML tags and attributes, making most operations much faster.
|
"dioxus-id",
|
||||||
///
|
"dioxus",
|
||||||
/// Interning takes < 1ms at the start of the app, but saves a *ton* of time later on.
|
"dioxus-event-click", // todo: more events
|
||||||
///
|
"click",
|
||||||
/// Eventually we might want to procedurally generate these strings for common words, phrases, and values.
|
// All the HTML Tags
|
||||||
pub(crate) fn intern_cached_strings() {
|
"a",
|
||||||
let cached_words = [
|
"abbr",
|
||||||
// Important tags to dioxus
|
"address",
|
||||||
"dioxus-id",
|
"area",
|
||||||
"dioxus",
|
"article",
|
||||||
"dioxus-event-click", // todo: more events
|
"aside",
|
||||||
"click",
|
"audio",
|
||||||
// All the HTML Tags
|
"b",
|
||||||
"a",
|
"base",
|
||||||
"abbr",
|
"bdi",
|
||||||
"address",
|
"bdo",
|
||||||
"area",
|
"big",
|
||||||
"article",
|
"blockquote",
|
||||||
"aside",
|
"body",
|
||||||
"audio",
|
"br",
|
||||||
"b",
|
"button",
|
||||||
"base",
|
"canvas",
|
||||||
"bdi",
|
"caption",
|
||||||
"bdo",
|
"cite",
|
||||||
"big",
|
"code",
|
||||||
"blockquote",
|
"col",
|
||||||
"body",
|
"colgroup",
|
||||||
"br",
|
"command",
|
||||||
"button",
|
"data",
|
||||||
"canvas",
|
"datalist",
|
||||||
"caption",
|
"dd",
|
||||||
"cite",
|
"del",
|
||||||
"code",
|
"details",
|
||||||
"col",
|
"dfn",
|
||||||
"colgroup",
|
"dialog",
|
||||||
"command",
|
"div",
|
||||||
"data",
|
"dl",
|
||||||
"datalist",
|
"dt",
|
||||||
"dd",
|
"em",
|
||||||
"del",
|
"embed",
|
||||||
"details",
|
"fieldset",
|
||||||
"dfn",
|
"figcaption",
|
||||||
"dialog",
|
"figure",
|
||||||
"div",
|
"footer",
|
||||||
"dl",
|
"form",
|
||||||
"dt",
|
"h1",
|
||||||
"em",
|
"h2",
|
||||||
"embed",
|
"h3",
|
||||||
"fieldset",
|
"h4",
|
||||||
"figcaption",
|
"h5",
|
||||||
"figure",
|
"h6",
|
||||||
"footer",
|
"head",
|
||||||
"form",
|
"header",
|
||||||
"h1",
|
"hr",
|
||||||
"h2",
|
"html",
|
||||||
"h3",
|
"i",
|
||||||
"h4",
|
"iframe",
|
||||||
"h5",
|
"img",
|
||||||
"h6",
|
"input",
|
||||||
"head",
|
"ins",
|
||||||
"header",
|
"kbd",
|
||||||
"hr",
|
"keygen",
|
||||||
"html",
|
"label",
|
||||||
"i",
|
"legend",
|
||||||
"iframe",
|
"li",
|
||||||
"img",
|
"link",
|
||||||
"input",
|
"main",
|
||||||
"ins",
|
"map",
|
||||||
"kbd",
|
"mark",
|
||||||
"keygen",
|
"menu",
|
||||||
"label",
|
"menuitem",
|
||||||
"legend",
|
"meta",
|
||||||
"li",
|
"meter",
|
||||||
"link",
|
"nav",
|
||||||
"main",
|
"noscript",
|
||||||
"map",
|
"object",
|
||||||
"mark",
|
"ol",
|
||||||
"menu",
|
"optgroup",
|
||||||
"menuitem",
|
"option",
|
||||||
"meta",
|
"output",
|
||||||
"meter",
|
"p",
|
||||||
"nav",
|
"param",
|
||||||
"noscript",
|
"picture",
|
||||||
"object",
|
"pre",
|
||||||
"ol",
|
"progress",
|
||||||
"optgroup",
|
"q",
|
||||||
"option",
|
"rp",
|
||||||
"output",
|
"rt",
|
||||||
"p",
|
"ruby",
|
||||||
"param",
|
"s",
|
||||||
"picture",
|
"samp",
|
||||||
"pre",
|
"script",
|
||||||
"progress",
|
"section",
|
||||||
"q",
|
"select",
|
||||||
"rp",
|
"small",
|
||||||
"rt",
|
"source",
|
||||||
"ruby",
|
"span",
|
||||||
"s",
|
"strong",
|
||||||
"samp",
|
"style",
|
||||||
"script",
|
"sub",
|
||||||
"section",
|
"summary",
|
||||||
"select",
|
"sup",
|
||||||
"small",
|
"table",
|
||||||
"source",
|
"tbody",
|
||||||
"span",
|
"td",
|
||||||
"strong",
|
"textarea",
|
||||||
"style",
|
"tfoot",
|
||||||
"sub",
|
"th",
|
||||||
"summary",
|
"thead",
|
||||||
"sup",
|
"time",
|
||||||
"table",
|
"title",
|
||||||
"tbody",
|
"tr",
|
||||||
"td",
|
"track",
|
||||||
"textarea",
|
"u",
|
||||||
"tfoot",
|
"ul",
|
||||||
"th",
|
"var",
|
||||||
"thead",
|
"video",
|
||||||
"time",
|
"wbr",
|
||||||
"title",
|
// All the event handlers
|
||||||
"tr",
|
"Attribute",
|
||||||
"track",
|
"accept",
|
||||||
"u",
|
"accept-charset",
|
||||||
"ul",
|
"accesskey",
|
||||||
"var",
|
"action",
|
||||||
"video",
|
"alt",
|
||||||
"wbr",
|
"async",
|
||||||
// All the event handlers
|
"autocomplete",
|
||||||
"Attribute",
|
"autofocus",
|
||||||
"accept",
|
"autoplay",
|
||||||
"accept-charset",
|
"charset",
|
||||||
"accesskey",
|
"checked",
|
||||||
"action",
|
"cite",
|
||||||
"alt",
|
"class",
|
||||||
"async",
|
"cols",
|
||||||
"autocomplete",
|
"colspan",
|
||||||
"autofocus",
|
"content",
|
||||||
"autoplay",
|
"contenteditable",
|
||||||
"charset",
|
"controls",
|
||||||
"checked",
|
"coords",
|
||||||
"cite",
|
"data",
|
||||||
"class",
|
"data-*",
|
||||||
"cols",
|
"datetime",
|
||||||
"colspan",
|
"default",
|
||||||
"content",
|
"defer",
|
||||||
"contenteditable",
|
"dir",
|
||||||
"controls",
|
"dirname",
|
||||||
"coords",
|
"disabled",
|
||||||
"data",
|
"download",
|
||||||
"data-*",
|
"draggable",
|
||||||
"datetime",
|
"enctype",
|
||||||
"default",
|
"for",
|
||||||
"defer",
|
"form",
|
||||||
"dir",
|
"formaction",
|
||||||
"dirname",
|
"headers",
|
||||||
"disabled",
|
"height",
|
||||||
"download",
|
"hidden",
|
||||||
"draggable",
|
"high",
|
||||||
"enctype",
|
"href",
|
||||||
"for",
|
"hreflang",
|
||||||
"form",
|
"http-equiv",
|
||||||
"formaction",
|
"id",
|
||||||
"headers",
|
"ismap",
|
||||||
"height",
|
"kind",
|
||||||
"hidden",
|
"label",
|
||||||
"high",
|
"lang",
|
||||||
"href",
|
"list",
|
||||||
"hreflang",
|
"loop",
|
||||||
"http-equiv",
|
"low",
|
||||||
"id",
|
"max",
|
||||||
"ismap",
|
"maxlength",
|
||||||
"kind",
|
"media",
|
||||||
"label",
|
"method",
|
||||||
"lang",
|
"min",
|
||||||
"list",
|
"multiple",
|
||||||
"loop",
|
"muted",
|
||||||
"low",
|
"name",
|
||||||
"max",
|
"novalidate",
|
||||||
"maxlength",
|
"onabort",
|
||||||
"media",
|
"onafterprint",
|
||||||
"method",
|
"onbeforeprint",
|
||||||
"min",
|
"onbeforeunload",
|
||||||
"multiple",
|
"onblur",
|
||||||
"muted",
|
"oncanplay",
|
||||||
"name",
|
"oncanplaythrough",
|
||||||
"novalidate",
|
"onchange",
|
||||||
"onabort",
|
"onclick",
|
||||||
"onafterprint",
|
"oncontextmenu",
|
||||||
"onbeforeprint",
|
"oncopy",
|
||||||
"onbeforeunload",
|
"oncuechange",
|
||||||
"onblur",
|
"oncut",
|
||||||
"oncanplay",
|
"ondblclick",
|
||||||
"oncanplaythrough",
|
"ondrag",
|
||||||
"onchange",
|
"ondragend",
|
||||||
"onclick",
|
"ondragenter",
|
||||||
"oncontextmenu",
|
"ondragleave",
|
||||||
"oncopy",
|
"ondragover",
|
||||||
"oncuechange",
|
"ondragstart",
|
||||||
"oncut",
|
"ondrop",
|
||||||
"ondblclick",
|
"ondurationchange",
|
||||||
"ondrag",
|
"onemptied",
|
||||||
"ondragend",
|
"onended",
|
||||||
"ondragenter",
|
"onerror",
|
||||||
"ondragleave",
|
"onfocus",
|
||||||
"ondragover",
|
"onhashchange",
|
||||||
"ondragstart",
|
"oninput",
|
||||||
"ondrop",
|
"oninvalid",
|
||||||
"ondurationchange",
|
"onkeydown",
|
||||||
"onemptied",
|
"onkeypress",
|
||||||
"onended",
|
"onkeyup",
|
||||||
"onerror",
|
"onload",
|
||||||
"onfocus",
|
"onloadeddata",
|
||||||
"onhashchange",
|
"onloadedmetadata",
|
||||||
"oninput",
|
"onloadstart",
|
||||||
"oninvalid",
|
"onmousedown",
|
||||||
"onkeydown",
|
"onmousemove",
|
||||||
"onkeypress",
|
"onmouseout",
|
||||||
"onkeyup",
|
"onmouseover",
|
||||||
"onload",
|
"onmouseup",
|
||||||
"onloadeddata",
|
"onmousewheel",
|
||||||
"onloadedmetadata",
|
"onoffline",
|
||||||
"onloadstart",
|
"ononline",
|
||||||
"onmousedown",
|
"onpageshow",
|
||||||
"onmousemove",
|
"onpaste",
|
||||||
"onmouseout",
|
"onpause",
|
||||||
"onmouseover",
|
"onplay",
|
||||||
"onmouseup",
|
"onplaying",
|
||||||
"onmousewheel",
|
"onprogress",
|
||||||
"onoffline",
|
"onratechange",
|
||||||
"ononline",
|
"onreset",
|
||||||
"onpageshow",
|
"onresize",
|
||||||
"onpaste",
|
"onscroll",
|
||||||
"onpause",
|
"onsearch",
|
||||||
"onplay",
|
"onseeked",
|
||||||
"onplaying",
|
"onseeking",
|
||||||
"onprogress",
|
"onselect",
|
||||||
"onratechange",
|
"onstalled",
|
||||||
"onreset",
|
"onsubmit",
|
||||||
"onresize",
|
"onsuspend",
|
||||||
"onscroll",
|
"ontimeupdate",
|
||||||
"onsearch",
|
"ontoggle",
|
||||||
"onseeked",
|
"onunload",
|
||||||
"onseeking",
|
"onvolumechange",
|
||||||
"onselect",
|
"onwaiting",
|
||||||
"onstalled",
|
"onwheel",
|
||||||
"onsubmit",
|
"open",
|
||||||
"onsuspend",
|
"optimum",
|
||||||
"ontimeupdate",
|
"pattern",
|
||||||
"ontoggle",
|
"placeholder",
|
||||||
"onunload",
|
"poster",
|
||||||
"onvolumechange",
|
"preload",
|
||||||
"onwaiting",
|
"readonly",
|
||||||
"onwheel",
|
"rel",
|
||||||
"open",
|
"required",
|
||||||
"optimum",
|
"reversed",
|
||||||
"pattern",
|
"rows",
|
||||||
"placeholder",
|
"rowspan",
|
||||||
"poster",
|
"sandbox",
|
||||||
"preload",
|
"scope",
|
||||||
"readonly",
|
"selected",
|
||||||
"rel",
|
"shape",
|
||||||
"required",
|
"size",
|
||||||
"reversed",
|
"sizes",
|
||||||
"rows",
|
"span",
|
||||||
"rowspan",
|
"spellcheck",
|
||||||
"sandbox",
|
"src",
|
||||||
"scope",
|
"srcdoc",
|
||||||
"selected",
|
"srclang",
|
||||||
"shape",
|
"srcset",
|
||||||
"size",
|
"start",
|
||||||
"sizes",
|
"step",
|
||||||
"span",
|
"style",
|
||||||
"spellcheck",
|
"tabindex",
|
||||||
"src",
|
"target",
|
||||||
"srcdoc",
|
"title",
|
||||||
"srclang",
|
"translate",
|
||||||
"srcset",
|
"type",
|
||||||
"start",
|
"usemap",
|
||||||
"step",
|
"value",
|
||||||
"style",
|
"width",
|
||||||
"tabindex",
|
"wrap",
|
||||||
"target",
|
"0",
|
||||||
"title",
|
"1",
|
||||||
"translate",
|
"2",
|
||||||
"type",
|
"3",
|
||||||
"usemap",
|
"4",
|
||||||
"value",
|
"5",
|
||||||
"width",
|
"6",
|
||||||
"wrap",
|
"7",
|
||||||
"0",
|
"8",
|
||||||
"1",
|
"9",
|
||||||
"2",
|
"10",
|
||||||
"3",
|
"11",
|
||||||
"4",
|
"12",
|
||||||
"5",
|
"13",
|
||||||
"6",
|
"14",
|
||||||
"7",
|
];
|
||||||
"8",
|
|
||||||
"9",
|
|
||||||
"10",
|
|
||||||
"11",
|
|
||||||
"12",
|
|
||||||
"13",
|
|
||||||
"14",
|
|
||||||
];
|
|
||||||
|
|
||||||
for s in cached_words {
|
|
||||||
wasm_bindgen::intern(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
pub struct WebConfig {
|
pub struct WebConfig {
|
||||||
pub(crate) hydrate: bool,
|
pub(crate) hydrate: bool,
|
||||||
pub(crate) rootname: String,
|
pub(crate) rootname: String,
|
||||||
|
pub(crate) cached_strings: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WebConfig {
|
impl Default for WebConfig {
|
||||||
@@ -18,6 +19,7 @@ impl Default for WebConfig {
|
|||||||
Self {
|
Self {
|
||||||
hydrate: false,
|
hydrate: false,
|
||||||
rootname: "main".to_string(),
|
rootname: "main".to_string(),
|
||||||
|
cached_strings: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,4 +43,12 @@ impl WebConfig {
|
|||||||
self.rootname = name.into();
|
self.rootname = name.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the name of the element that Dioxus will use as the root.
|
||||||
|
///
|
||||||
|
/// This is akint to calling React.render() on the element with the specified name.
|
||||||
|
pub fn with_string_cache(mut self, cache: Vec<String>) -> Self {
|
||||||
|
self.cached_strings = cache;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+14
-14
@@ -56,7 +56,6 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
pub use crate::cfg::WebConfig;
|
pub use crate::cfg::WebConfig;
|
||||||
use crate::dom::load_document;
|
use crate::dom::load_document;
|
||||||
use cache::intern_cached_strings;
|
|
||||||
use dioxus::SchedulerMsg;
|
use dioxus::SchedulerMsg;
|
||||||
use dioxus::VirtualDom;
|
use dioxus::VirtualDom;
|
||||||
pub use dioxus_core as dioxus;
|
pub use dioxus_core as dioxus;
|
||||||
@@ -82,15 +81,15 @@ mod ric_raf;
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// dioxus_web::launch(App, |c| c);
|
/// dioxus_web::launch(App);
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// static App: FC<()> = |cx, props| {
|
/// static App: Component<()> = |cx, props| {
|
||||||
/// rsx!(cx, div {"hello world"})
|
/// rsx!(cx, div {"hello world"})
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn launch(root_component: Component<()>, configuration: impl FnOnce(WebConfig) -> WebConfig) {
|
pub fn launch(root_component: Component<()>) {
|
||||||
launch_with_props(root_component, (), configuration)
|
launch_with_props(root_component, (), |c| c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Launches the VirtualDOM from the specified component function and props.
|
/// Launches the VirtualDOM from the specified component function and props.
|
||||||
@@ -109,7 +108,7 @@ pub fn launch(root_component: Component<()>, configuration: impl FnOnce(WebConfi
|
|||||||
/// name: String
|
/// name: String
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// static App: FC<RootProps> = |cx, props| {
|
/// static App: Component<RootProps> = |cx, props| {
|
||||||
/// rsx!(cx, div {"hello {props.name}"})
|
/// rsx!(cx, div {"hello {props.name}"})
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
@@ -140,7 +139,12 @@ pub fn launch_with_props<T, F>(
|
|||||||
pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T, cfg: WebConfig) {
|
pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T, cfg: WebConfig) {
|
||||||
let mut dom = VirtualDom::new_with_props(root, root_props);
|
let mut dom = VirtualDom::new_with_props(root, root_props);
|
||||||
|
|
||||||
intern_cached_strings();
|
for s in crate::cache::BUILTIN_INTERNED_STRINGS {
|
||||||
|
wasm_bindgen::intern(s);
|
||||||
|
}
|
||||||
|
for s in &cfg.cached_strings {
|
||||||
|
wasm_bindgen::intern(s);
|
||||||
|
}
|
||||||
|
|
||||||
let should_hydrate = cfg.hydrate;
|
let should_hydrate = cfg.hydrate;
|
||||||
|
|
||||||
@@ -158,7 +162,6 @@ pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T
|
|||||||
// hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
|
// hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
|
||||||
// ElementIds should already line up because the web_sys dom has already loaded elements with the DioxusID into memory
|
// ElementIds should already line up because the web_sys dom has already loaded elements with the DioxusID into memory
|
||||||
if !should_hydrate {
|
if !should_hydrate {
|
||||||
// log::info!("Applying rebuild edits..., {:?}", mutations);
|
|
||||||
websys_dom.process_edits(&mut mutations.edits);
|
websys_dom.process_edits(&mut mutations.edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,20 +172,17 @@ pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T
|
|||||||
// if there is work then this future resolves immediately.
|
// if there is work then this future resolves immediately.
|
||||||
dom.wait_for_work().await;
|
dom.wait_for_work().await;
|
||||||
|
|
||||||
// // wait for the mainthread to schedule us in
|
// wait for the mainthread to schedule us in
|
||||||
// let mut deadline = work_loop.wait_for_idle_time().await;
|
let mut deadline = work_loop.wait_for_idle_time().await;
|
||||||
|
|
||||||
// run the virtualdom work phase until the frame deadline is reached
|
// run the virtualdom work phase until the frame deadline is reached
|
||||||
let mutations = dom.work_with_deadline(|| false);
|
let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some());
|
||||||
// // run the virtualdom work phase until the frame deadline is reached
|
|
||||||
// let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some());
|
|
||||||
|
|
||||||
// wait for the animation frame to fire so we can apply our changes
|
// wait for the animation frame to fire so we can apply our changes
|
||||||
work_loop.wait_for_raf().await;
|
work_loop.wait_for_raf().await;
|
||||||
|
|
||||||
for mut edit in mutations {
|
for mut edit in mutations {
|
||||||
// actually apply our changes during the animation frame
|
// actually apply our changes during the animation frame
|
||||||
// log::info!("Applying change edits..., {:?}", edit);
|
|
||||||
websys_dom.process_edits(&mut edit.edits);
|
websys_dom.process_edits(&mut edit.edits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user