Feat: push new component API and context API

This commit is contained in:
Jonathan Kelley 2021-02-11 23:03:01 -05:00
parent 8329268d39
commit 125f5426a4
14 changed files with 432 additions and 264 deletions

2
packages/core/.vscode/spellright.dict vendored Normal file
View file

@ -0,0 +1,2 @@
Dodrio
VDoms

View file

@ -0,0 +1,19 @@
//! An alternative function syntax
//!
use std::marker::PhantomData;
use dioxus_core::prelude::VNode;
fn main() {}
struct Context2<'a> {
_p: PhantomData<&'a ()>,
}
type FC2<'a, 'b, 'c: 'a + 'b, P> = fn(Context2<'a>, &'b P) -> VNode<'c>;
static Example: FC2<()> = |ctx, props| {
//
todo!()
};

View file

@ -1,4 +1,4 @@
use std::{marker::PhantomData, ops::Deref}; use std::{borrow::Borrow, marker::PhantomData, ops::Deref};
use builder::{button, div}; use builder::{button, div};
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
@ -7,34 +7,27 @@ fn main() {}
struct SomeContext { struct SomeContext {
items: Vec<String>, items: Vec<String>,
} }
/*
desired behavior:
free to move the context guard around struct Props {
not free to move contents of context guard into closure name: String,
}
rules: #[allow(unused)]
can deref in a function static Example: FC<Props> = |ctx, props| {
cannot drag the refs into the closure w let value = ctx.use_context(|c: &SomeContext| c.items.last().unwrap());
*/
static Example: FC<()> = |ctx| { ctx.view(move |bump| {
// let value = use_context(&ctx, |ctx: &SomeContext| ctx.items.last().unwrap());
// let b = *value;
// let v2 = *value;
let cb = move |e| {
// let g = b.as_str();
// let g = (v2).as_str();
// let g = (value).as_str();
// let g = b.as_str();
};
// let r = *value;
// let r2 = *r;
ctx.view(|bump| {
button(bump) button(bump)
.listeners([builder::on(bump, "click", cb)]) .on("click", move |_| {
// //
println!("Value is {}", props.name);
println!("Value is {}", value.as_str());
println!("Value is {}", *value);
})
//
.on("click", move |_| {
println!("Value is {}", props.name);
})
.finish() .finish()
}) })
// ctx.view(html! { // ctx.view(html! {
@ -48,24 +41,3 @@ static Example: FC<()> = |ctx| {
// </div> // </div>
// }) // })
}; };
#[derive(Clone, Copy)]
struct ContextGuard<T> {
val: PhantomData<T>,
}
impl<'a, T> Deref for ContextGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
todo!()
}
}
fn use_context<'scope, 'dope, 'a, P: Properties, I, O: 'a>(
ctx: &'scope Context<P>,
s: fn(&'a I) -> O,
) -> &'scope ContextGuard<O> {
// ) -> &'scope ContextGuard<O> {
todo!()
}

View file

@ -1,9 +1,101 @@
#![allow(unused, non_upper_case_globals)] // #![allow(unused, non_upper_case_globals)]
use bumpalo::Bump; // use bumpalo::Bump;
use dioxus_core::nodebuilder::*; // use dioxus_core::nodebuilder::*;
use dioxus_core::prelude::VNode; // use dioxus_core::prelude::VNode;
use dioxus_core::prelude::*; // use dioxus_core::prelude::*;
use once_cell::sync::{Lazy, OnceCell}; // use once_cell::sync::{Lazy, OnceCell};
use std::{collections::HashMap, future::Future, marker::PhantomData}; use std::{collections::HashMap, future::Future, marker::PhantomData};
use std::ops::Deref;
/*
A guard over underlying T that provides access in callbacks via "Copy"
*/
// #[derive(Clone)]
struct ContextGuard2<T> {
_val: std::marker::PhantomData<T>,
}
impl<T> Clone for ContextGuard2<T> {
// we aren't cloning the underlying data so clone isn't necessary
fn clone(&self) -> Self {
todo!()
}
}
impl<T> Copy for ContextGuard2<T> {}
impl<T> ContextGuard2<T> {
fn get<'a>(&'a self) -> ContextLock<'a, T> {
todo!()
}
}
struct ContextLock<'a, T> {
_val: std::marker::PhantomData<&'a T>,
}
impl<'a, T: 'a + 'static> Deref for ContextLock<'a, T> {
type Target = T;
fn deref<'b>(&'b self) -> &'b T {
todo!()
}
}
/*
The source of the data that gives out context guards
*/
struct Context<'a> {
_p: std::marker::PhantomData<&'a ()>,
}
impl<'a> Context<'a> {
fn use_context<'b, I, O: 'b>(&self, f: fn(&'b I) -> O) -> ContextGuard2<O> {
todo!()
}
fn add_listener(&self, f: impl Fn(()) + 'static) {
todo!()
}
fn view(self, f: impl FnOnce(&'a String) + 'a) {}
// fn view(self, f: impl for<'b> FnOnce(&'a String) + 'a) {}
// fn view(self, f: impl for<'b> FnOnce(&'b String) + 'a) {}
}
struct Example {
value: String,
}
/*
Example compiling
*/
fn t<'a>(ctx: Context<'a>) {
let value = ctx.use_context(|b: &Example| &b.value);
// Works properly, value is moved by copy into the closure
let refed = value.get();
println!("Value is {}", refed.as_str());
let r2 = refed.as_str();
ctx.add_listener(move |_| {
// let val = value.get().as_str();
let val2 = r2.as_bytes();
});
// let refed = value.deref();
// returns &String
// returns &String
// let refed = value.deref(); // returns &String
// let refed = value.deref(); // returns &String
// Why does this work? This closure should be static but is holding a reference to refed
// The context guard is meant to prevent any references moving into the closure
// if the references move they might become invalid due to mutlithreading issues
ctx.add_listener(move |_| {
// let val = value.as_str();
// let val2 = refed.as_bytes();
});
ctx.view(move |b| {});
}
fn main() {} fn main() {}

View file

@ -4,19 +4,28 @@ use dioxus_core::prelude::*;
fn main() {} fn main() {}
static Example: FC<()> = |ctx| { static Example: FC<()> = |ctx, props| {
let (val1, set_val1) = use_state(&ctx, || "b1"); todo!()
// let (val1, set_val1) = use_state(&ctx, || "b1");
ctx.view(html! { // ctx.view(|bump| {
<div> // builder::button(bump)
<button onclick={move |_| set_val1("b1")}> "Set value to b1" </button> // .on("click", move |c| {
<button onclick={move |_| set_val1("b2")}> "Set value to b2" </button> // //
<button onclick={move |_| set_val1("b3")}> "Set value to b3" </button> // println!("Value is {}", val1);
<div> // })
<p> "Value is: {val1}" </p> // .finish()
</div> // })
</div> // ctx.view(html! {
}) // <div>
// <button onclick={move |_| set_val1("b1")}> "Set value to b1" </button>
// <button onclick={move |_| set_val1("b2")}> "Set value to b2" </button>
// <button onclick={move |_| set_val1("b3")}> "Set value to b3" </button>
// <div>
// <p> "Value is: {val1}" </p>
// </div>
// </div>
// })
}; };
use use_state_def::use_state; use use_state_def::use_state;
@ -52,8 +61,8 @@ mod use_state_def {
/// } /// }
/// } /// }
/// ``` /// ```
pub fn use_state<'b, 'a, P: Properties + 'static, T: 'static, F: FnOnce() -> T + 'static>( pub fn use_state<'b, 'a, T: 'static, F: FnOnce() -> T + 'static>(
ctx: &'b Context<'a, P>, ctx: &'b Context<'a>,
initial_state_fn: F, initial_state_fn: F,
) -> (&'a T, &'a impl Fn(T)) { ) -> (&'a T, &'a impl Fn(T)) {
ctx.use_hook( ctx.use_hook(
@ -116,8 +125,8 @@ mod use_ref_def {
/// To read the value, borrow the ref. /// To read the value, borrow the ref.
/// To change it, use modify. /// To change it, use modify.
/// Modifications to this value do not cause updates to the component /// Modifications to this value do not cause updates to the component
pub fn use_ref<'a, P, T: 'static>( pub fn use_ref<'a, T: 'static>(
ctx: &'a Context<'a, P>, ctx: &'a Context<'a>,
initial_state_fn: impl FnOnce() -> T + 'static, initial_state_fn: impl FnOnce() -> T + 'static,
) -> &'a UseRef<T> { ) -> &'a UseRef<T> {
ctx.use_hook(|| UseRef::new(initial_state_fn()), |state| &*state, |_| {}) ctx.use_hook(|| UseRef::new(initial_state_fn()), |state| &*state, |_| {})

View file

@ -25,7 +25,7 @@ impl Properties for Props {
} }
} }
static Example: FC<Props> = |ctx| { static Example: FC<Props> = |ctx, props| {
ctx.view(html! { ctx.view(html! {
<div> <div>
<h1> </h1> <h1> </h1>

View file

@ -43,20 +43,25 @@ mod tests {
todo!() todo!()
} }
fn test_component(ctx: Context<()>) -> VNode { static TestComponent: FC<()> = |ctx, props| {
ctx.view(html! {<div> </div> }) //
} ctx.view(html! {
<div>
</div>
})
};
fn test_component2(ctx: Context<()>) -> VNode { static TestComponent2: FC<()> = |ctx, props| {
//
ctx.view(|bump: &Bump| VNode::text("blah")) ctx.view(|bump: &Bump| VNode::text("blah"))
} };
#[test] #[test]
fn ensure_types_work() { fn ensure_types_work() {
let bump = Bump::new(); let bump = Bump::new();
// Happiness! The VNodes are now allocated onto the bump vdom // Happiness! The VNodes are now allocated onto the bump vdom
let _ = test_static_fn(&bump, test_component); let _ = test_static_fn(&bump, TestComponent);
let _ = test_static_fn(&bump, test_component2); let _ = test_static_fn(&bump, TestComponent2);
} }
} }

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::scope::Hook;
use crate::{inner::Scope, nodes::VNode}; use crate::{inner::Scope, nodes::VNode};
use bumpalo::Bump; use bumpalo::Bump;
use hooks::Hook;
use std::{ use std::{
any::TypeId, cell::RefCell, future::Future, marker::PhantomData, sync::atomic::AtomicUsize, any::TypeId, cell::RefCell, future::Future, marker::PhantomData, sync::atomic::AtomicUsize,
}; };
@ -26,9 +26,10 @@ use std::{
/// ``` /// ```
// todo: force lifetime of source into T as a valid lifetime too // todo: force lifetime of source into T as a valid lifetime too
// it's definitely possible, just needs some more messing around // it's definitely possible, just needs some more messing around
pub struct Context<'src, PropType> { pub struct Context<'src> {
// pub struct Context<'src, PropType> {
/// Direct access to the properties used to create this component. /// Direct access to the properties used to create this component.
pub props: PropType, // pub props: &'src PropType,
pub idx: AtomicUsize, pub idx: AtomicUsize,
// Borrowed from scope // Borrowed from scope
@ -41,7 +42,8 @@ pub struct Context<'src, PropType> {
pub _p: std::marker::PhantomData<&'src ()>, pub _p: std::marker::PhantomData<&'src ()>,
} }
impl<'a, PropType> Context<'a, PropType> { impl<'a> Context<'a> {
// impl<'a, PropType> Context<'a, PropType> {
/// Access the children elements passed into the component /// Access the children elements passed into the component
pub fn children(&self) -> Vec<VNode> { pub fn children(&self) -> Vec<VNode> {
todo!("Children API not yet implemented for component Context") todo!("Children API not yet implemented for component Context")
@ -73,10 +75,12 @@ impl<'a, PropType> Context<'a, PropType> {
/// ctx.view(lazy_tree) /// ctx.view(lazy_tree)
/// } /// }
///``` ///```
pub fn view(self, v: impl FnOnce(&'a Bump) -> VNode<'a>) -> VNode<'a> { pub fn view(self, v: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> VNode<'a> {
todo!() todo!()
} }
pub fn callback(&self, f: impl Fn(()) + 'static) {}
/// Create a suspended component from a future. /// Create a suspended component from a future.
/// ///
/// When the future completes, the component will be renderered /// When the future completes, the component will be renderered
@ -86,7 +90,25 @@ impl<'a, PropType> Context<'a, PropType> {
) -> VNode<'a> { ) -> VNode<'a> {
todo!() todo!()
} }
}
pub mod hooks {
//! This module provides internal state management functionality for Dioxus components
//!
use super::*;
pub struct Hook(pub Box<dyn std::any::Any>);
impl Hook {
pub fn new(state: Box<dyn std::any::Any>) -> Self {
Self(state)
}
}
impl<'a> Context<'a> {
// impl<'a, P> Context<'a> {
// impl<'a, P> Context<'a, P> {
/// use_hook provides a way to store data between renders for functional components. /// 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 /// 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<'internal, 'scope, InternalHookState: 'static, Output: 'internal>(
@ -144,4 +166,129 @@ impl<'a, PropType> Context<'a, PropType> {
runner(internal_state) runner(internal_state)
} }
}
}
mod context_api {
//! Context API
//!
//! The context API provides a mechanism for components to borrow state from other components higher in the tree.
//! By combining the Context API and the Subscription API, we can craft ergonomic global state management systems.
//!
//! This API is inherently dangerous because we could easily cause UB by allowing &T and &mut T to exist at the same time.
//! To prevent this, we expose the RemoteState<T> and RemoteLock<T> types which act as a form of reverse borrowing.
//! This is very similar to RwLock, except that RemoteState is copy-able. Unlike RwLock, derefing RemoteState can
//! cause panics if the pointer is null. In essence, we sacrifice the panic protection for ergonomics, but arrive at
//! a similar end result.
//!
//! Instead of placing the onus on the receiver of the data to use it properly, we wrap the source object in a
//! "shield" where gaining &mut access can only be done if no active StateGuards are open. This would fail and indicate
//! a failure of implementation.
//!
//!
use super::*;
use std::{marker::PhantomPinned, ops::Deref};
pub struct RemoteState<T> {
inner: *const T,
}
impl<T> Copy for RemoteState<T> {}
impl<T> Clone for RemoteState<T> {
fn clone(&self) -> Self {
Self { inner: self.inner }
}
}
static DEREF_ERR_MSG: &'static str = r#"""
[ERROR]
This state management implementation is faulty. Report an issue on whatever implementation is using this.
Context should *never* be dangling!. If a Context is torn down, so should anything that references it.
"""#;
impl<T> Deref for RemoteState<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// todo!
// Try to borrow the underlying context manager, register this borrow with the manager as a "weak" subscriber.
// This will prevent the panic and ensure the pointer still exists.
// For now, just get an immutable reference to the underlying context data.
//
// It's important to note that ContextGuard is not a public API, and can only be made from UseContext.
// This guard should only be used in components, and never stored in hooks
unsafe {
match self.inner.as_ref() {
Some(ptr) => ptr,
None => panic!(DEREF_ERR_MSG),
}
}
}
}
impl<'a> super::Context<'a> {
// impl<'a, P> super::Context<'a, P> {
pub fn use_context<I, O>(&'a self, narrow: impl Fn(&'_ I) -> &'_ O) -> RemoteState<O> {
todo!()
}
}
/// # SAFETY ALERT
///
/// The underlying context mechanism relies on mutating &mut T while &T is held by components in the tree.
/// By definition, this is UB. Therefore, implementing use_context should be done with upmost care to invalidate and
/// prevent any code where &T is still being held after &mut T has been taken and T has been mutated.
///
/// While mutating &mut T while &T is captured by listeners, we can do any of:
/// 1) Prevent those listeners from being called and avoid "producing" UB values
/// 2) Delete instances of closures where &T is captured before &mut T is taken
/// 3) Make clones of T to preserve the original &T.
/// 4) Disable any &T remotely (like RwLock, RefCell, etc)
///
/// To guarantee safe usage of state management solutions, we provide Dioxus-Reducer and Dioxus-Dataflow built on the
/// SafeContext API. This should provide as an example of how to implement context safely for 3rd party state management.
///
/// It's important to recognize that while safety is a top concern for Dioxus, ergonomics do take prescendence.
/// Contrasting with the JS ecosystem, Rust is faster, but actually "less safe". JS is, by default, a "safe" language.
/// However, it does not protect you against data races: the primary concern for 3rd party implementers of Context.
///
/// We guarantee that any &T will remain consistent throughout the life of the Virtual Dom and that
/// &T is owned by components owned by the VirtualDom. Therefore, it is impossible for &T to:
/// - be dangling or unaligned
/// - produce an invalid value
/// - produce uninitialized memory
///
/// The only UB that is left to the implementer to prevent are Data Races.
///
/// Here's a strategy that is UB:
/// 1. &T is handed out via use_context
/// 2. an event is reduced against the state
/// 3. An &mut T is taken
/// 4. &mut T is mutated.
///
/// Now, any closures that caputed &T are subject to a data race where they might have skipped checks and UB
/// *will* affect the program.
///
/// Here's a strategy that's not UB (implemented by SafeContext):
/// 1. ContextGuard<T> is handed out via use_context.
/// 2. An event is reduced against the state.
/// 3. The state is cloned.
/// 4. All subfield selectors are evaluated and then diffed with the original.
/// 5. Fields that have changed have their ContextGuard poisoned, revoking their ability to take &T.a.
/// 6. The affected fields of Context are mutated.
/// 7. Scopes with poisoned guards are regenerated so they can take &T.a again, calling their lifecycle.
///
/// In essence, we've built a "partial borrowing" mechanism for Context objects.
///
/// =================
/// nb
/// =================
/// If you want to build a state management API directly and deal with all the unsafe and UB, we provide
/// `use_context_unchecked` with all the stability with *no* guarantess of Data Race protection. You're on
/// your own to not affect user applications.
///
/// - Dioxus reducer is built on the safe API and provides a useful but slightly limited API.
/// - Dioxus Dataflow is built on the unsafe API and provides an even snazzier API than Dioxus Reducer.
fn blah() {}
} }

View file

@ -1,87 +0,0 @@
//! Context API
//!
//! The context API provides a mechanism for components to grab
//!
//!
//!
use std::marker::PhantomPinned;
/// Any item that works with app
pub trait AppContext {}
#[derive(Copy, Clone)]
pub struct ContextGuard<'a, T> {
inner: *mut T,
_p: std::marker::PhantomData<&'a ()>,
}
impl<'a, PropType> super::context::Context<'a, PropType> {
/// # SAFETY ALERT
///
/// The underlying context mechanism relies on mutating &mut T while &T is held by components in the tree.
/// By definition, this is UB. Therefore, implementing use_context should be done with upmost care to invalidate and
/// prevent any code where &T is still being held after &mut T has been taken and T has been mutated.
///
/// While mutating &mut T while &T is captured by listeners, we can do any of:
/// 1) Prevent those listeners from being called and avoid "producing" UB values
/// 2) Delete instances of closures where &T is captured before &mut T is taken
/// 3) Make clones of T to preserve the original &T.
///
/// To guarantee safe usage of state management solutions, we provide Dioxus-Reducer and Dioxus-Dataflow built on the
/// SafeContext API. This should provide as an example of how to implement context safely for 3rd party state management.
///
/// It's important to recognize that while safety is a top concern for Dioxus, ergonomics do take prescendence.
/// Contrasting with the JS ecosystem, Rust is faster, but actually "less safe". JS is, by default, a "safe" language.
/// However, it does not protect you against data races: the primary concern for 3rd party implementers of Context.
///
/// We guarantee that any &T will remain consistent throughout the life of the Virtual Dom and that
/// &T is owned by components owned by the VirtualDom. Therefore, it is impossible for &T to:
/// - be dangling or unaligned
/// - produce an invalid value
/// - produce uninitialized memory
///
/// The only UB that is left to the implementer to prevent are Data Races.
///
/// Here's a strategy that is UB:
/// 1. &T is handed out via use_context
/// 2. an event is reduced against the state
/// 3. An &mut T is taken
/// 4. &mut T is mutated.
///
/// Now, any closures that caputed &T are subject to a data race where they might have skipped checks and UB
/// *will* affect the program.
///
/// Here's a strategy that's not UB (implemented by SafeContext):
/// 1. ContextGuard<T> is handed out via use_context.
/// 2. An event is reduced against the state.
/// 3. The state is cloned.
/// 4. All subfield selectors are evaluated and then diffed with the original.
/// 5. Fields that have changed have their ContextGuard poisoned, revoking their ability to take &T.a.
/// 6. The affected fields of Context are mutated.
/// 7. Scopes with poisoned guards are regenerated so they can take &T.a again, calling their lifecycle.
///
/// In essence, we've built a "partial borrowing" mechanism for Context objects.
///
/// =================
/// nb
/// =================
/// If you want to build a state management API directly and deal with all the unsafe and UB, we provide
/// `use_context_unchecked` with all the stability with *no* guarantess of Data Race protection. You're on
/// your own to not affect user applications.
///
/// - Dioxus reducer is built on the safe API and provides a useful but slightly limited API.
/// - Dioxus Dataflow is built on the unsafe API and provides an even snazzier API than Dioxus Reducer.
pub fn use_context<C: AppContext>(&'a self) -> C {
todo!()
}
pub unsafe fn use_context_unchecked<C: AppContext>() {}
}
struct SafeContext<T> {
value: T,
// This context is pinned
_pinned: PhantomPinned,
}

View file

@ -28,7 +28,11 @@ mod tests {
#[test] #[test]
fn ensure_creation() -> Result<(), ()> { fn ensure_creation() -> Result<(), ()> {
let mut dom = VirtualDom::new(|ctx| ctx.view(html! { <div>"hello world" </div> })); let mut dom = VirtualDom::new(|ctx, props| {
//
ctx.view(html! { <div>"hello world" </div> })
});
dom.progress()?; dom.progress()?;
Ok(()) Ok(())
} }

View file

@ -31,8 +31,8 @@
//! #[derive(Properties)] //! #[derive(Properties)]
//! struct Props { name: String } //! struct Props { name: String }
//! //!
//! static Example: FC<Props> = |ctx| { //! static Example: FC<Props> = |ctx, props| {
//! html! { <div> "Hello {ctx.props.name}!" </div> } //! html! { <div> "Hello {props.name}!" </div> }
//! } //! }
//! ``` //! ```
//! //!
@ -40,7 +40,7 @@
//! ``` //! ```
//! use dioxus_core::prelude::*; //! use dioxus_core::prelude::*;
//! //!
//! #[functional_component] //! #[fc]
//! static Example: FC = |ctx, name: String| { //! static Example: FC = |ctx, name: String| {
//! html! { <div> "Hello {name}!" </div> } //! html! { <div> "Hello {name}!" </div> }
//! } //! }
@ -67,7 +67,6 @@
pub mod component; pub mod component;
pub mod context; pub mod context;
pub mod contextapi;
pub mod debug_renderer; pub mod debug_renderer;
pub mod events; pub mod events;
pub mod nodebuilder; pub mod nodebuilder;
@ -83,15 +82,18 @@ pub mod builder {
// types used internally that are important // types used internally that are important
pub(crate) mod inner { pub(crate) mod inner {
pub use crate::component::{Component, Properties}; pub use crate::component::{Component, Properties};
use crate::context::hooks::Hook;
pub use crate::context::Context; pub use crate::context::Context;
use crate::nodes; use crate::nodes;
pub use crate::scope::{Hook, Scope}; pub use crate::scope::Scope;
pub use crate::virtual_dom::VirtualDom; pub use crate::virtual_dom::VirtualDom;
pub use nodes::*; pub use nodes::*;
// pub use nodes::iterables::IterableNodes; // pub use nodes::iterables::IterableNodes;
/// This type alias is an internal way of abstracting over the static functions that represent components. /// 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, P>) -> VNode<'a>;
pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> VNode<'a>;
// pub type FC<P> = for<'a> fn(Context<'a, P>) -> VNode<'a>;
// TODO @Jon, fix this // TODO @Jon, fix this
// hack the VNode type until VirtualNode is fixed in the macro crate // hack the VNode type until VirtualNode is fixed in the macro crate
@ -115,7 +117,7 @@ pub mod prelude {
// pub use nodes::iterables::IterableNodes; // pub use nodes::iterables::IterableNodes;
/// This type alias is an internal way of abstracting over the static functions that represent components. /// 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, P>) -> VNode<'a>; pub use crate::inner::FC;
// TODO @Jon, fix this // TODO @Jon, fix this
// hack the VNode type until VirtualNode is fixed in the macro crate // hack the VNode type until VirtualNode is fixed in the macro crate

View file

@ -334,14 +334,15 @@ where
/// .finish(); /// .finish();
/// ``` /// ```
#[inline] #[inline]
pub fn on<F>(mut self, event: &'a str, callback: F) -> Self pub fn on(mut self, event: &'a str, callback: impl Fn(()) + 'a) -> Self
where // pub fn on<F>(mut self, event: &'a str, callback: impl Fn(()) -> () + 'static) -> Self
F: Fn(()) + 'a, // F: Fn(()) + 'static,
// F: Fn(()) + 'a,
{ {
self.listeners.push(Listener { // self.listeners.push(Listener {
event, // event,
callback: self.bump.alloc(callback), // callback: self.bump.alloc(callback),
}); // });
self self
} }
} }
@ -1068,10 +1069,12 @@ pub fn attr<'a>(name: &'a str, value: &'a str) -> Attribute<'a> {
/// // do something when a click happens... /// // do something when a click happens...
/// }); /// });
/// ``` /// ```
pub fn on<'a, 'b, F: 'b>(bump: &'a Bump, event: &'a str, callback: F) -> Listener<'a> pub fn on<'a, 'b, F: 'static>(
where bump: &'a Bump,
'b: 'a + 'static, event: &'a str,
F: Fn(()) + 'b, callback: impl Fn(()) + 'static,
) -> Listener<'a>
// F: Fn(()) + 'b,
{ {
Listener { Listener {
event, event,

View file

@ -1,9 +1,11 @@
use crate::context::hooks::Hook;
use crate::inner::*;
use crate::nodes::VNode; use crate::nodes::VNode;
use crate::prelude::*;
use bumpalo::Bump; use bumpalo::Bump;
use generational_arena::Index; use generational_arena::Index;
use std::{ use std::{
any::TypeId, cell::RefCell, future::Future, marker::PhantomData, sync::atomic::AtomicUsize, any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData,
sync::atomic::AtomicUsize,
}; };
/// Every component in Dioxus is represented by a `Scope`. /// Every component in Dioxus is represented by a `Scope`.
@ -48,13 +50,15 @@ impl Scope {
pub fn create_context<'runner, T: Properties>( pub fn create_context<'runner, T: Properties>(
&'runner mut self, &'runner mut self,
components: &'runner generational_arena::Arena<Scope>, components: &'runner generational_arena::Arena<Scope>,
) -> Context<T> { props: &'runner T,
) -> Context {
// ) -> Context<T> {
Context { Context {
_p: PhantomData {}, _p: PhantomData {},
arena: &self.hook_arena, arena: &self.hook_arena,
hooks: &self.hooks, hooks: &self.hooks,
idx: 0.into(), idx: 0.into(),
props: T::new(), // props,
components, components,
} }
} }
@ -62,32 +66,27 @@ impl Scope {
/// Create a new context and run the component with references from the Virtual Dom /// Create a new context and run the component with references from the Virtual Dom
/// This function downcasts the function pointer based on the stored props_type /// This function downcasts the function pointer based on the stored props_type
fn run<T: 'static>(&self, f: FC<T>) {} fn run<T: 'static>(&self, f: FC<T>) {}
fn call<'a, T: Properties + 'static>(&'a mut self, val: T) {
if self.props_type == TypeId::of::<T>() {
/*
SAFETY ALERT
This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
we transmute the function back using the props as reference.
This is safe because we check that the generic type matches before casting.
*/
let caller = unsafe { std::mem::transmute::<*const i32, FC<T>>(self.caller) };
// let ctx = self.create_context::<T>();
// // TODO: do something with these nodes
// let nodes = caller(ctx);
} else {
panic!("Do not try to use `call` on Scopes with the wrong props type")
}
}
} }
pub struct Hook(pub Box<dyn std::any::Any>); mod bad_unsafety {
// todo
// fn call<'a, T: Properties + 'static>(&'a mut self, val: T) {
// if self.props_type == TypeId::of::<T>() {
// /*
// SAFETY ALERT
impl Hook { // This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
pub fn new(state: Box<dyn std::any::Any>) -> Self { // We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
Self(state) // we transmute the function back using the props as reference.
}
// This is safe because we check that the generic type matches before casting.
// */
// let caller = unsafe { std::mem::transmute::<*const i32, FC<T>>(self.caller) };
// // let ctx = self.create_context::<T>();
// // // TODO: do something with these nodes
// // let nodes = caller(ctx);
// } else {
// panic!("Do not try to use `call` on Scopes with the wrong props type")
// }
// }
} }

View file

@ -142,7 +142,8 @@ impl<P: Properties + 'static> VirtualDom<P> {
pub async fn progess_completely() {} pub async fn progess_completely() {}
/// Create a new context object for a given component and scope /// Create a new context object for a given component and scope
fn new_context<T: Properties>(&self) -> Context<T> { fn new_context<T: Properties>(&self) -> Context {
// fn new_context<T: Properties>(&self) -> Context<T> {
todo!() todo!()
} }