Просмотр исходного кода

Feat: push new component API and context API

Jonathan Kelley 4 лет назад
Родитель
Сommit
125f542

+ 2 - 0
packages/core/.vscode/spellright.dict

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

+ 19 - 0
packages/core/examples/alternative.rs

@@ -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!()
+};

+ 18 - 46
packages/core/examples/contextapi.rs

@@ -1,4 +1,4 @@
-use std::{marker::PhantomData, ops::Deref};
+use std::{borrow::Borrow, marker::PhantomData, ops::Deref};
 
 use builder::{button, div};
 use dioxus_core::prelude::*;
@@ -7,34 +7,27 @@ fn main() {}
 struct SomeContext {
     items: Vec<String>,
 }
-/*
-desired behavior:
 
-free to move the context guard around
-not free to move contents of context guard into closure
-
-rules:
-can deref in a function
-cannot drag the refs into the closure w
-*/
-
-static Example: FC<()> = |ctx| {
-    // let value = use_context(&ctx, |ctx: &SomeContext| ctx.items.last().unwrap());
+struct Props {
+    name: String,
+}
 
-    // 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;
+#[allow(unused)]
+static Example: FC<Props> = |ctx, props| {
+    let value = ctx.use_context(|c: &SomeContext| c.items.last().unwrap());
 
-    ctx.view(|bump| {
+    ctx.view(move |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()
     })
     // ctx.view(html! {
@@ -48,24 +41,3 @@ static Example: FC<()> = |ctx| {
     //     </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!()
-}

+ 98 - 6
packages/core/examples/dummy.rs

@@ -1,9 +1,101 @@
-#![allow(unused, non_upper_case_globals)]
-use bumpalo::Bump;
-use dioxus_core::nodebuilder::*;
-use dioxus_core::prelude::VNode;
-use dioxus_core::prelude::*;
-use once_cell::sync::{Lazy, OnceCell};
+// #![allow(unused, non_upper_case_globals)]
+// use bumpalo::Bump;
+// use dioxus_core::nodebuilder::*;
+// use dioxus_core::prelude::VNode;
+// use dioxus_core::prelude::*;
+// use once_cell::sync::{Lazy, OnceCell};
 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() {}

+ 25 - 16
packages/core/examples/listener.rs

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

+ 1 - 1
packages/core/examples/step.rs

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

+ 12 - 7
packages/core/src/component.rs

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

+ 202 - 55
packages/core/src/context.rs

@@ -1,7 +1,7 @@
 use crate::prelude::*;
-use crate::scope::Hook;
 use crate::{inner::Scope, nodes::VNode};
 use bumpalo::Bump;
+use hooks::Hook;
 use std::{
     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
 // 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.
-    pub props: PropType,
+    // pub props: &'src PropType,
     pub idx: AtomicUsize,
 
     // Borrowed from scope
@@ -41,7 +42,8 @@ pub struct Context<'src, PropType> {
     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
     pub fn children(&self) -> Vec<VNode> {
         todo!("Children API not yet implemented for component Context")
@@ -73,10 +75,12 @@ impl<'a, PropType> Context<'a, PropType> {
     ///     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!()
     }
 
+    pub fn callback(&self, f: impl Fn(()) + 'static) {}
+
     /// Create a suspended component from a future.
     ///
     /// When the future completes, the component will be renderered
@@ -86,62 +90,205 @@ impl<'a, PropType> Context<'a, PropType> {
     ) -> VNode<'a> {
         todo!()
     }
+}
 
-    /// 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>(
-        &'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,
-        // 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),
-    ) -> Output {
-        let raw_hook = {
-            let idx = self.idx.load(std::sync::atomic::Ordering::Relaxed);
-
-            // Mutate hook list if necessary
-            let mut hooks = self.hooks.borrow_mut();
-
-            // Initialize the hook by allocating it in the typed arena.
-            // We get a reference from the arena which is owned by the component scope
-            // This is valid because "Context" is only valid while the scope is borrowed
-            if idx >= hooks.len() {
-                let new_state = initializer();
-                let boxed_state: Box<dyn std::any::Any> = Box::new(new_state);
-                let hook = self.arena.alloc(Hook::new(boxed_state));
-
-                // Push the raw pointer instead of the &mut
-                // A "poor man's OwningRef"
-                hooks.push(hook);
-            }
-            self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
+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.
+        /// 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>(
+            &'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,
+            // 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),
+        ) -> Output {
+            let raw_hook = {
+                let idx = self.idx.load(std::sync::atomic::Ordering::Relaxed);
+
+                // Mutate hook list if necessary
+                let mut hooks = self.hooks.borrow_mut();
+
+                // Initialize the hook by allocating it in the typed arena.
+                // We get a reference from the arena which is owned by the component scope
+                // This is valid because "Context" is only valid while the scope is borrowed
+                if idx >= hooks.len() {
+                    let new_state = initializer();
+                    let boxed_state: Box<dyn std::any::Any> = Box::new(new_state);
+                    let hook = self.arena.alloc(Hook::new(boxed_state));
+
+                    // Push the raw pointer instead of the &mut
+                    // A "poor man's OwningRef"
+                    hooks.push(hook);
+                }
+                self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
+
+                *hooks.get(idx).unwrap()
+            };
+
+            /*
+            ** UNSAFETY ALERT **
+            Here, we dereference a raw pointer. Normally, we aren't guaranteed that this is okay.
+
+            However, typed-arena gives a mutable reference to the stored data which is stable for any inserts
+            into the arena. During the first call of the function, we need to add the mutable reference given to us by
+            the arena into our list of hooks. The arena provides stability of the &mut references and is only deallocated
+            when the component itself is deallocated.
+
+            This is okay because:
+            - The lifetime of the component arena is tied to the lifetime of these raw hooks
+            - Usage of the raw hooks is tied behind the Vec refcell
+            - Output is static, meaning it  can't take a reference to the data
+            - 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 internal_state = borrowed_hook.0.downcast_mut::<InternalHookState>().unwrap();
+
+            runner(internal_state)
+        }
+    }
+}
 
-            *hooks.get(idx).unwrap()
-        };
+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::*;
 
-        /*
-        ** UNSAFETY ALERT **
-        Here, we dereference a raw pointer. Normally, we aren't guaranteed that this is okay.
+    use std::{marker::PhantomPinned, ops::Deref};
 
-        However, typed-arena gives a mutable reference to the stored data which is stable for any inserts
-        into the arena. During the first call of the function, we need to add the mutable reference given to us by
-        the arena into our list of hooks. The arena provides stability of the &mut references and is only deallocated
-        when the component itself is deallocated.
+    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 }
+        }
+    }
 
-        This is okay because:
-        - The lifetime of the component arena is tied to the lifetime of these raw hooks
-        - Usage of the raw hooks is tied behind the Vec refcell
-        - Output is static, meaning it can't take a reference to the data
-        - 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() };
+    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.
+"""#;
 
-        let internal_state = borrowed_hook.0.downcast_mut::<InternalHookState>().unwrap();
+    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),
+                }
+            }
+        }
+    }
 
-        runner(internal_state)
+    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() {}
 }

+ 0 - 87
packages/core/src/contextapi.rs

@@ -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,
-}

+ 5 - 1
packages/core/src/debug_renderer.rs

@@ -28,7 +28,11 @@ mod tests {
 
     #[test]
     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()?;
         Ok(())
     }

+ 9 - 7
packages/core/src/lib.rs

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

+ 14 - 11
packages/core/src/nodebuilder.rs

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

+ 28 - 29
packages/core/src/scope.rs

@@ -1,9 +1,11 @@
+use crate::context::hooks::Hook;
+use crate::inner::*;
 use crate::nodes::VNode;
-use crate::prelude::*;
 use bumpalo::Bump;
 use generational_arena::Index;
 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`.
@@ -48,13 +50,15 @@ impl Scope {
     pub fn create_context<'runner, T: Properties>(
         &'runner mut self,
         components: &'runner generational_arena::Arena<Scope>,
-    ) -> Context<T> {
+        props: &'runner T,
+    ) -> Context {
+        // ) -> Context<T> {
         Context {
             _p: PhantomData {},
             arena: &self.hook_arena,
             hooks: &self.hooks,
             idx: 0.into(),
-            props: T::new(),
+            // props,
             components,
         }
     }
@@ -62,32 +66,27 @@ impl Scope {
     /// 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
     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 {
-    pub fn new(state: Box<dyn std::any::Any>) -> Self {
-        Self(state)
-    }
+    //         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")
+    //     }
+    // }
 }

+ 2 - 1
packages/core/src/virtual_dom.rs

@@ -142,7 +142,8 @@ impl<P: Properties + 'static> VirtualDom<P> {
     pub async fn progess_completely() {}
 
     /// 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!()
     }