Feat: WIP ctx

This commit is contained in:
Jonathan Kelley 2021-02-20 21:59:16 -05:00
parent b3e6886351
commit 7a6aabe4f3
18 changed files with 430 additions and 99 deletions

View file

@ -26,3 +26,4 @@ rei
RefMut
diffed
datafetching
partialeq

View file

@ -1,3 +1,8 @@
# Project: Web-View 🤲 🍨
> Proof of concept: stream render edits from server to client
- [x] Prove that the diffing and patching framework can support patch streaming
# Project: Live-View 🤲 🍨
> Combine the server and client into a single file :)

View file

@ -287,3 +287,71 @@ On the actual diffing level, we're using the diffing algorithm pulled from Dodri
## Patch Stream
One of the most important parts of Dioxus is the ability to stream patches from server to client. However, this inherently has challenges where raw VNodes attach listeners to themselves, and are therefore not serializable.
### How do properties work?
How should properties passing work? Should we directly call the child? Should we box the props? Should we replace the pops inside the box?
Here's (generally) my head is at:
Components need to store their props on them if they want to be updated remotely. These props *can* be updated after the fact.
Perf concerns:
unnecessary function runs
- list-y components
- hook calls?
- making vnodes?
Does any of this matter?
Should we just run any component we see, immediately and imperatively? That will cause checks throughout the whole tree, no matter where the update occurred
https://calendar.perfplanet.com/2013/diff/
Here's how react does it:
Any "dirty" node causes an entire subtree render. Calling "setState" at the very top will cascade all the way down. This is particularly bad for this component design:
```rust
static APP: FC<()> = |ctx, props| {
let title = use_context(Title);
ctx.view(html!{
<div>
<h1> "{title}"</h1>
<HeavyList /> // VComponent::new(|| (FC, PropsForFc)) -> needs a context to immediately update the component's props imperatively? store the props in a box on bump? store the props on the child?
// if props didnt change, then let the refernece stay invalid?.... no, cant do that, bump gets reset
// immediately update props on the child component if it can be found? -> interesting, feels wrong, but faster, at the very least.
// can box on bump for the time being (fast enough), and then move it over? during the comparison phase? props only need to matter
// cant downcast (can with transmute, but yikes)
// how does chain borrowing work? a -> b -> c -> d
// if b gets marked as dirty, then c and d are invalidated (semantically, UB, but not *bad* UB, just data races)
// make props static? -> easy to move, gross to use
//
// treat like a context selector?
// use_props::<P>(2)
// child_props: Map<Scope, Box<dyn Props>>
// vs children: BTreeSet<Scope> -> to get nth
</div>
})
};
static HEAVY_LIST: FC<()> = |ctx, props| {
ctx.view({
{0.100.map(i => <BigElement >)}
})
};
```
An update to the use_context subscription will mark the node as dirty. The node then is forced to re-analyze HeavyList, even though HeavyList did not change. We should automatically implement this suppression knowing that props are immutable and can be partialeq.
## FC Layout
The FC layout was altered to make life easier for us inside the VirtualDom. The "view" function returns an unbounded DomTree object. Calling the "view" function is unsafe under the hood, but prevents lifetimes from leaking out of the function call. Plus, it's easier to write. Because there are no lifetimes on the output (occur purely under the hood), we can escape needing to annotate them.
```rust
fn component(ctx: Context, props: &Props) -> DomTree {
}
```
The DomTree object purely represents a viewable "key". It also forces components to use the "view" function as there is no other way to generate the DomTree object. Because the DomTree is a required type of FC, we can guarantee the same usage and flow patterns for all components.

View file

@ -34,5 +34,5 @@ longest-increasing-subsequence = "0.1.0"
serde = { version = "1.0.123", features = ["derive"] }
log = "0.4.14"
pretty_env_logger = "0.4.0"
ouroboros = "0.8.0"
# ouroboros = "0.8.0"
# hashbrown = { version = "0.9.1", features = ["bumpalo"] }

View file

@ -3,17 +3,69 @@
use std::marker::PhantomData;
use dioxus_core::prelude::VNode;
use bumpalo::Bump;
use dioxus_core::prelude::{DomTree, VNode};
fn main() {}
struct Context2<'a> {
_p: PhantomData<&'a ()>,
struct Context2<'a, P> {
props: &'a P, // _p: PhantomData<&'a ()>,
}
impl<'a, P> Context2<'a, P> {
fn view(&self, f: impl FnOnce(&'a Bump) -> VNode<'a>) -> DTree {
DTree {}
}
type FC2<'a, 'b, 'c: 'a + 'b, P> = fn(Context2<'a>, &'b P) -> VNode<'c>;
pub fn use_hook<'scope, InternalHookState: 'static, Output: 'a>(
&'scope self,
initializer: impl FnOnce() -> InternalHookState,
runner: impl FnOnce(&'a mut InternalHookState) -> Output,
cleanup: impl FnOnce(InternalHookState),
) -> Output {
todo!()
}
}
static Example: FC2<()> = |ctx, props| {
struct DTree;
type FC2<'a, T: 'a> = fn(&'a Context2<T>) -> DTree;
struct Props {
c: String,
}
static Example: FC2<Props> = |ctx| {
let val = use_state(&ctx, || String::from("asd"));
ctx.view(move |b| {
let g: VNode<'_> = virtual_child(b, Props2 { a: val }, alt_child);
//
dioxus_core::nodebuilder::div(b)
.child(dioxus_core::nodebuilder::text(ctx.props.c.as_str()))
.child(g)
.finish()
})
};
fn virtual_child<'a, T: 'a>(bump: &'a Bump, props: T, f: FC2<T>) -> VNode<'a> {
todo!()
}
struct Props2<'a> {
a: &'a String,
}
fn alt_child<'a>(ctx: &Context2<'a, Props2<'_>>) -> DomTree {
todo!()
}
static CHILD: FC2<Props2> = |ctx| {
//
todo!()
};
fn use_state<'a, 'c, P, T: 'static, F: FnOnce() -> T>(
ctx: &'a Context2<'a, P>,
initial_state_fn: F,
) -> (&'a T) {
todo!()
}

View file

@ -0,0 +1,32 @@
use std::marker::PhantomData;
fn main() {}
trait Props<'parent> {}
struct SomeProps<'p> {
text: &'p str,
}
impl<'p> Props<'p> for SomeProps<'p> {}
struct OutputNode<'a> {
_p: PhantomData<&'a ()>,
}
// combine reference to self (borrowed from self) and referenfce to parent (borrowed from parent)
// borrow chain looks like 'p + 's -> 'p + 's -> 'p + 's
// always adding new lifetimes from self into the mix
// what does a "self" lifetime mean?
// a "god" gives us our data
// the god's lifetime is tied to Context, and the borrowed props object
// for the sake of simplicity, we just clobber lifetimes.
// user functions are just lies and we abuse lifetimes.
// everything is managed at runtime because that's how we make something ergonomc
// lifetime management in dioxus is just cheating around the rules
// our kind god manages lifetimes for us so we don't have to, thanks god
fn something<'s>(props: &'s SomeProps<'s>) -> OutputNode<'s> {
todo!()
}
// type BC<'p, P: Props<'p>> = for<'a, 'b, 'c> fn(&'a P<'b>) -> OutputNode<'c>;

View file

@ -16,16 +16,18 @@ fn main() -> Result<(), ()> {
Ok(())
}
#[derive(Debug)]
struct Props {
name: String,
}
impl Properties for Props {
fn call(&self, ptr: *const ()) {}
// fn new() -> Self {
// todo!()
// impl Properties for Props {
// fn call(&self, ptr: *const ()) {}
// // fn new() -> Self {
// // todo!()
// // }
// }
}
static Example: FC<Props> = |ctx, props| {
ctx.view(html! {

View file

@ -2,44 +2,47 @@
//! for components to be used within Nodes.
//!
use std::fmt::Debug;
use crate::innerlude::*;
/// The `Component` trait refers to any struct or funciton that can be used as a component
/// We automatically implement Component for FC<T>
pub trait Component {
type Props: Properties;
fn builder(&'static self) -> Self::Props;
}
// pub trait Component {
// type Props: Properties<'static>;
// fn builder(&'static self) -> Self::Props;
// }
// Auto implement component for a FC
// Calling the FC is the same as "rendering" it
impl<P: Properties> Component for FC<P> {
type Props = P;
// // Auto implement component for a FC
// // Calling the FC is the same as "rendering" it
// impl<P: Properties<'static>> Component for FC<P> {
// type Props = P;
fn builder(&self) -> Self::Props {
todo!()
}
}
// fn builder(&self) -> Self::Props {
// todo!()
// }
// }
/// The `Properties` trait defines any struct that can be constructed using a combination of default / optional fields.
/// Components take a "properties" object
pub trait Properties // where
// Self: Sized,
{
fn call(&self, ptr: *const ()) {}
}
// pub trait Properties<'a>
// where
// Self: Debug,
// {
// fn call(&self, ptr: *const ()) {}
// }
// Auto implement for no-prop components
impl Properties for () {
fn call(&self, ptr: *const ()) {}
}
// // Auto implement for no-prop components
// impl<'a> Properties<'a> for () {
// fn call(&self, ptr: *const ()) {}
// }
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::bumpalo::Bump;
fn test_static_fn<'a, P: Properties>(b: &'a Bump, r: FC<P>) -> VNode<'a> {
fn test_static_fn<'a, P>(b: &'a Bump, r: FC<P>) -> VNode<'a> {
todo!()
}

View file

@ -68,8 +68,12 @@ impl<'a> Context<'a> {
/// ctx.view(lazy_tree)
/// }
///```
pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> VNode<'a> {
lazy_nodes(self.bump)
pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> DomTree {
// pub fn view(self, lazy_nodes: impl for<'b> FnOnce(&'b Bump) -> VNode<'b> + 'a + 'p) -> DomTree {
// pub fn view<'p>(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a + 'p) -> DomTree {
// pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> VNode<'a> {
let g = lazy_nodes(self.bump);
DomTree {}
}
pub fn callback(&self, f: impl Fn(()) + 'static) {}
@ -103,12 +107,12 @@ pub mod hooks {
/// TODO: @jon, rework this so we dont have to use unsafe to make hooks and then return them
/// use_hook provides a way to store data between renders for functional components.
/// todo @jon: ensure the hook arena is stable with pin or is stable by default
pub fn use_hook<'internal, 'scope, InternalHookState: 'static, Output: 'internal>(
pub fn use_hook<'scope, InternalHookState: 'static, Output: 'a>(
&'scope self,
// The closure that builds the hook state
initializer: impl FnOnce() -> InternalHookState,
// The closure that takes the hookstate and returns some value
runner: impl FnOnce(&'internal mut InternalHookState) -> Output,
runner: impl FnOnce(&'a mut InternalHookState) -> Output,
// The closure that cleans up whatever mess is left when the component gets torn down
// TODO: add this to the "clean up" group for when the component is dropped
cleanup: impl FnOnce(InternalHookState),
@ -152,7 +156,7 @@ pub mod hooks {
- We don't expose the raw hook pointer outside of the scope of use_hook
- The reference is tied to context, meaning it can only be used while ctx is around to free it
*/
let borrowed_hook: &'internal mut _ = unsafe { raw_hook.as_mut().unwrap() };
let borrowed_hook: &'a mut _ = unsafe { raw_hook.as_mut().unwrap() };
let internal_state = borrowed_hook.0.downcast_mut::<InternalHookState>().unwrap();

View file

@ -3,7 +3,7 @@
//!
//! Renderers don't actually need to own the virtual dom (it's up to the implementer).
use crate::prelude::{Properties, VirtualDom};
use crate::prelude::VirtualDom;
pub struct DebugRenderer {
vdom: VirtualDom,

View file

@ -40,8 +40,8 @@ mod use_state_def {
/// }
/// }
/// ```
pub fn use_state<'a, T: 'static, F: FnOnce() -> T + 'static>(
ctx: &'_ Context<'a>,
pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>(
ctx: &'c Context<'a>,
initial_state_fn: F,
) -> (&'a T, &'a impl Fn(T)) {
ctx.use_hook(
@ -93,7 +93,7 @@ mod use_ref_def {
}
}
fn modify(&self, modifier: impl FnOnce(&mut T)) {
pub fn modify(&self, modifier: impl FnOnce(&mut T)) {
let mut val = self._current.borrow_mut();
let val_as_ref = val.deref_mut();
modifier(val_as_ref);

View file

@ -87,7 +87,7 @@ pub mod builder {
// types used internally that are important
pub(crate) mod innerlude {
pub(crate) use crate::component::{Component, Properties};
// pub(crate) use crate::component::Properties;
use crate::context::hooks::Hook;
pub(crate) use crate::context::Context;
pub(crate) use crate::error::{Error, Result};
@ -99,7 +99,15 @@ pub(crate) mod innerlude {
// pub use nodes::iterables::IterableNodes;
/// This type alias is an internal way of abstracting over the static functions that represent components.
pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> VNode<'a>;
pub type FC<P> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree;
mod fc2 {
use super::*;
}
// pub type FC<'a, P: 'a> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree;
// pub type FC<P> = for<'scope, 'r> fn(Context<'scope>, &'scope P) -> DomTree;
// pub type FC<P> = for<'scope, 'r> fn(Context<'scope>, &'r P) -> VNode<'scope>;
// pub type FC<P> = for<'scope, 'r> fn(Context<'scope>, &'r P) -> VNode<'scope>;
// pub type FC<P> = for<'a> fn(Context<'a, P>) -> VNode<'a>;
// TODO @Jon, fix this
@ -116,7 +124,7 @@ pub(crate) mod innerlude {
/// Re-export common types for ease of development use.
/// Essential when working with the html! macro
pub mod prelude {
pub use crate::component::{Component, Properties};
// pub use crate::component::Properties;
pub use crate::context::Context;
use crate::nodes;
pub use crate::virtual_dom::VirtualDom;

View file

@ -1,6 +1,7 @@
//! Helpers for building virtual DOM VNodes.
use crate::{
innerlude::VComponent,
nodes::{Attribute, Listener, NodeKey, VNode},
prelude::VElement,
};
@ -1079,3 +1080,8 @@ pub fn on<'a, 'b, F: 'static>(
callback: bump.alloc(callback),
}
}
pub fn virtual_child<'a, T>(bump: &'a Bump, props: T, f: crate::innerlude::FC<T>) -> VNode<'a> {
todo!()
// VNode::Component()
}

View file

@ -12,6 +12,10 @@ pub use velement::{Attribute, Listener, NodeKey};
pub use vnode::VNode;
pub use vtext::VText;
/// A domtree represents the result of "Viewing" the context
/// It's a placeholder over vnodes, to make working with lifetimes easier
pub struct DomTree;
/// Tools for the base unit of the virtual dom - the VNode
/// VNodes are intended to be quickly-allocated, lightweight enum values.
///
@ -20,6 +24,7 @@ pub use vtext::VText;
mod vnode {
use super::*;
#[derive(Debug)]
pub enum VNode<'src> {
/// An element node (node type `ELEMENT_NODE`).
Element(&'src VElement<'src>),
@ -92,8 +97,9 @@ mod vnode {
mod velement {
use super::*;
use std::collections::HashMap;
use std::{collections::HashMap, fmt::Debug};
#[derive(Debug)]
pub struct VElement<'a> {
/// Elements have a tag name, zero or more attributes, and zero or more
pub key: NodeKey,
@ -186,6 +192,13 @@ mod velement {
// /// The callback to invoke when the event happens.
pub(crate) callback: &'bump (dyn Fn(())),
}
impl Debug for Listener<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Listener")
.field("event", &self.event)
.finish()
}
}
/// The key for keyed children.
///
@ -231,7 +244,7 @@ mod velement {
}
mod vtext {
#[derive(PartialEq, serde::Serialize, serde::Deserialize)]
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct VText<'bump> {
pub text: &'bump str,
}
@ -251,28 +264,30 @@ mod vtext {
/// Virtual Components for custom user-defined components
/// Only supports the functional syntax
mod vcomponent {
use crate::innerlude::{Properties, FC};
use crate::innerlude::FC;
use std::{any::TypeId, fmt, future::Future, marker::PhantomData};
use super::VNode;
#[derive(Debug)]
pub struct VComponent<'src> {
_p: PhantomData<&'src ()>,
props: Box<dyn Properties>,
props: Box<dyn std::any::Any>,
// props: Box<dyn Properties + 'src>,
caller: *const (),
}
impl<'a> VComponent<'a> {
pub fn new<P: Properties + 'static>(caller: FC<P>, props: P) -> Self {
pub fn new<P>(caller: FC<P>, props: P) -> Self {
let caller = caller as *const ();
let props = Box::new(props);
Self {
_p: PhantomData {},
props,
caller,
}
todo!()
// Self {
// _p: PhantomData {},
// props,
// caller,
// }
}
}
}

View file

@ -33,39 +33,39 @@ pub struct Scope {
// Map to the parent
pub parent: Option<Index>,
// todo, do better with the active frame stuff
// somehow build this vnode with a lifetime tied to self
// This root node has "static" lifetime, but it's really not static.
// It's goverened by the oldest of the two frames and is switched every time a new render occurs
// Use this node as if it were static is unsafe, and needs to be fixed with ourborous or owning ref
// ! do not copy this reference are things WILL break !
pub frames: ActiveFrame,
// IE Which listeners need to be woken up?
pub listeners: Vec<Box<dyn Fn()>>,
// lying, cheating reference >:(
pub props: Box<dyn std::any::Any>,
// pub props: Box<dyn Properties>,
//
pub props_type: TypeId,
pub caller: *const i32,
// pub props_type: TypeId,
pub caller: *const (),
}
impl Scope {
// create a new scope from a function
pub fn new<T: 'static>(
// pub(crate) fn new<T: 'static>(
f: FC<T>,
props: impl Properties + 'static,
pub fn new<'a, P1, P2: 'static>(
// pub fn new<'a, P: Properties, PFree: P + 'a, PLocked: P + 'static>(
f: FC<P1>,
props: P1,
parent: Option<Index>,
) -> Self {
) -> Self
// where
// PFree: 'a,
// PLocked: 'static,
{
// Capture the props type
let props_type = TypeId::of::<T>();
// let props_type = TypeId::of::<P>();
let hook_arena = typed_arena::Arena::new();
let hooks = RefCell::new(Vec::new());
// Capture the caller
let caller = f as *const i32;
let caller = f as *const ();
let listeners = Vec::new();
@ -80,12 +80,20 @@ impl Scope {
};
let frames = ActiveFrame::from_frames(old_frame, new_frame);
// box the props
let props = Box::new(props);
// erase the lifetime
// we'll manage this with dom lifecycle
let props = unsafe { std::mem::transmute::<_, Box<P2>>(props) };
// todo!()
Self {
hook_arena,
hooks,
props_type,
// props_type,
caller,
frames,
listeners,
@ -94,13 +102,10 @@ impl Scope {
}
}
/// Update this component's props with a new set of props
/// Update this component's props with a new set of props, remotely
///
///
pub(crate) fn update_props<P: Properties + Sized + 'static>(
&mut self,
new_props: Box<P>,
) -> crate::error::Result<()> {
pub(crate) fn update_props<'a, P>(&self, new_props: P) -> crate::error::Result<()> {
Ok(())
}
@ -108,7 +113,7 @@ impl Scope {
/// This function downcasts the function pointer based on the stored props_type
///
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
pub fn run<'bump, P: Properties + Sized + 'static>(&'bump mut self) {
pub fn run<'bump, PLocked: Sized + 'static>(&'bump mut self) {
let frame = {
let frame = self.frames.next();
frame.bump.reset();
@ -123,6 +128,18 @@ impl Scope {
_p: PhantomData {},
};
unsafe {
// we use plocked to be able to remove the borrowed lifetime
// these lifetimes could be very broken, so we need to dynamically manage them
let caller = std::mem::transmute::<*const (), FC<PLocked>>(self.caller);
let props = self.props.downcast_ref::<PLocked>().unwrap();
let nodes: DomTree = caller(ctx, props);
todo!("absorb domtree into self")
// let nodes: VNode<'bump> = caller(ctx, props);
// let unsafe_node = std::mem::transmute::<VNode<'bump>, VNode<'static>>(nodes);
// frame.head_node = unsafe_node;
}
/*
SAFETY ALERT
@ -135,9 +152,6 @@ impl Scope {
This is safe because we check that the generic type matches before casting.
*/
let caller = unsafe { std::mem::transmute::<*const i32, FC<P>>(self.caller) };
let nodes: VNode<'bump> = caller(ctx, self.props.downcast_ref::<P>().unwrap());
/*
SAFETY ALERT
@ -149,9 +163,6 @@ impl Scope {
- The VNode has a private API and can only be used from accessors.
- Public API cannot drop or destructure VNode
*/
let unsafe_node = unsafe { std::mem::transmute::<VNode<'bump>, VNode<'static>>(nodes) };
frame.head_node = unsafe_node;
}
/// Accessor to get the root node and its children (safely)\
@ -159,6 +170,7 @@ impl Scope {
pub fn current_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> {
self.frames.current_head_node()
}
pub fn prev_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> {
todo!()
}
@ -169,6 +181,12 @@ pub struct BumpFrame {
pub head_node: VNode<'static>,
}
// todo, do better with the active frame stuff
// somehow build this vnode with a lifetime tied to self
// This root node has "static" lifetime, but it's really not static.
// It's goverened by the oldest of the two frames and is switched every time a new render occurs
// Use this node as if it were static is unsafe, and needs to be fixed with ourborous or owning ref
// ! do not copy this reference are things WILL break !
pub struct ActiveFrame {
pub idx: AtomicUsize,
pub frames: [BumpFrame; 2],
@ -183,7 +201,7 @@ impl ActiveFrame {
}
fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
let cur_idx = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed);
let cur_idx = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed) % 1;
let raw_node = &self.frames[cur_idx];
unsafe {
let unsafe_head = &raw_node.head_node;
@ -202,3 +220,120 @@ impl ActiveFrame {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scope() {
let example: FC<()> = |ctx, props| {
use crate::builder::*;
ctx.view(|b| div(b).child(text("a")).finish())
};
let props = ();
let parent = None;
let scope = Scope::new::<(), ()>(example, props, parent);
}
#[derive(Debug)]
struct ExampleProps<'src> {
name: &'src String,
}
// impl<'src> Properties<'src> for ExampleProps<'src> {}
#[derive(Debug)]
struct EmptyProps<'src> {
name: &'src String,
}
// impl<'src> Properties<'src> for EmptyProps<'src> {}
use crate::{builder::*, hooks::use_ref};
fn example_fc<'a>(ctx: Context<'a>, props: &'a EmptyProps) -> DomTree {
// fn example_fc<'a>(ctx: Context<'a>, props: &'a EmptyProps<'a>) -> DomTree {
let (content, _): (&'a String, _) = crate::hooks::use_state(&ctx, || "abcd".to_string());
// let (content, _): (&'a String, _) = crate::hooks::use_state(&ctx, || "abcd".to_string());
// let (text, set_val) = crate::hooks::use_state(&ctx, || "abcd".to_string());
let childprops: ExampleProps<'a> = ExampleProps { name: content };
// let childprops: ExampleProps<'a> = ExampleProps { name: content };
ctx.view(move |b: &'a Bump| {
div(b)
.child(text(props.name))
// .child(text(props.name))
.child(virtual_child::<ExampleProps>(b, childprops, child_example))
// .child(virtual_child::<ExampleProps<'a>>(b, childprops, CHILD))
// as for<'scope> fn(Context<'_>, &'scope ExampleProps<'scope>) -> DomTree
// |ctx, pops| todo!(),
// .child(virtual_child::<'a>(
// b,
// child_example,
// ExampleProps { name: text },
// ))
.finish()
})
}
fn child_example(ctx: Context, props: &ExampleProps) -> DomTree {
ctx.view(move |b| {
div(b)
.child(text(props.name))
//
.finish()
})
}
static CHILD: FC<ExampleProps> = |ctx, props: &'_ ExampleProps| {
// todo!()
ctx.view(move |b| {
div(b)
.child(text(props.name))
//
.finish()
})
};
#[test]
fn test_borrowed_scope() {
// use crate::builder::*;
let example: FC<EmptyProps> = |ctx, props| {
// render counter
// let mut val = crate::hooks::use_ref(&ctx, || 0);
// val.modify(|f| {
// *f += 1;
// });
// dbg!(val.current());
// only needs to be valid when ran?
// can only borrow from parent?
// props are boxed in parent's scope?
// passing complex structures down to child?
// stored value
// let (text, set_val) = crate::hooks::use_state(&ctx, || "abcd".to_string());
ctx.view(move |b| {
todo!()
// div(b)
// // .child(text(props.name))
// // .child(virtual_child(b, CHILD, ExampleProps { name: val.as_str() }))
// .child(virtual_child(b, CHILD, ExampleProps { name: }))
// .finish()
})
};
let source_text = "abcd123".to_string();
let props = ExampleProps { name: &source_text };
// let parent = None;
// let mut scope =
// Scope::new::<EmptyProps, EmptyProps>(example, EmptyProps { name: &() }, parent);
// scope.run::<ExampleProps>();
// scope.run::<ExampleProps>();
// scope.run::<ExampleProps>();
// scope.run::<ExampleProps>();
// scope.run::<ExampleProps>();
// let nodes = scope.current_root_node();
// dbg!(nodes);
}
}

View file

@ -1,5 +1,9 @@
// use crate::{changelist::EditList, nodes::VNode};
use crate::{changelist::{self, EditList}, dodriodiff::DiffMachine, nodes::VNode};
use crate::{
changelist::{self, EditList},
dodriodiff::DiffMachine,
nodes::VNode,
};
use crate::{events::EventTrigger, innerlude::*};
use any::Any;
use bumpalo::Bump;
@ -51,7 +55,7 @@ impl VirtualDom {
///
/// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
/// to toss out the entire tree.
pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
pub fn new_with_props<P: 'static>(root: FC<P>, root_props: P) -> Self {
// 1. Create the component arena
// 2. Create the base scope (can never be removed)
// 3. Create the lifecycle queue
@ -82,7 +86,7 @@ impl VirtualDom {
}
/// With access to the virtual dom, schedule an update to the Root component's props
pub fn update_props<P: Properties + 'static>(&mut self, new_props: P) -> Result<()> {
pub fn update_props<P: 'static>(&mut self, new_props: P) -> Result<()> {
// Ensure the props match
if TypeId::of::<P>() != self._root_prop_type {
return Err(Error::WrongProps);
@ -262,10 +266,10 @@ pub enum LifecycleType {
Mount {
to: Option<Index>,
under: usize,
props: Box<dyn Properties>,
props: Box<dyn std::any::Any>,
},
PropsChanged {
props: Box<dyn Properties>,
props: Box<dyn std::any::Any>,
},
Rendered,
Mounted,
@ -279,12 +283,7 @@ pub enum LifecycleType {
impl LifecycleEvent {
// helper method for shortcutting to the enum type
// probably not necessary
fn mount<P: Properties + 'static>(
which: Index,
to: Option<Index>,
under: usize,
props: P,
) -> Self {
fn mount<P: 'static>(which: Index, to: Option<Index>, under: usize, props: P) -> Self {
Self {
component_index: which,
event_type: LifecycleType::Mount {

View file

@ -24,7 +24,7 @@ use dioxus::prelude::VElement;
pub use dioxus_core as dioxus;
use dioxus_core::{
events::EventTrigger,
prelude::{bumpalo::Bump, html, DiffMachine, Properties, VNode, VirtualDom, FC},
prelude::{bumpalo::Bump, html, DiffMachine, VNode, VirtualDom, FC},
};
use futures::{channel::mpsc, future, SinkExt, StreamExt};
use mpsc::UnboundedSender;
@ -50,7 +50,7 @@ impl WebsysRenderer {
/// Automatically progresses the creation of the VNode tree to completion.
///
/// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
pub fn new_with_props<T: 'static>(root: FC<T>, root_props: T) -> Self {
Self::from_vdom(VirtualDom::new_with_props(root, root_props))
}

View file

@ -7,7 +7,8 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
web-view = "0.7.2"
web-view = { git = "https://github.com/Boscop/web-view" }
# web-view = "0.7.2"
dioxus-core = { path = "../core" }
anyhow = "1.0.38"
argh = "0.1.4"