Przeglądaj źródła

Feat: overall API updates

Jonathan Kelley 4 lat temu
rodzic
commit
f47651b

+ 1 - 1
.vscode/settings.json

@@ -1,3 +1,3 @@
 {
-    "rust-analyzer.inlayHints.enable": true
+    "rust-analyzer.inlayHints.enable": false
 }

+ 3 - 2
CHANGELOG.md

@@ -28,8 +28,8 @@
 # Project: Hooks + Context + Subscriptions (TBD)
 > Implement the foundations for state management
 - [x] Implement context object
-- [ ] Implement use_state
-- [ ] Implement use_ref
+- [x] Implement use_state
+- [x] Implement use_ref
 - [ ] Implement use_reducer
 - [ ] Implement use_context
 
@@ -41,6 +41,7 @@
 
 # Project: Initial VDOM support (TBD)
 > Get the initial VDom + Event System + Patching + Diffing + Component framework up and running
+> Get a demo working using just the web
 - [x] (Core) Migrate virtual node into new VNode type
 - [x] (Core) Arena allocate VNodes
 - [x] (Core) Allow VNodes to borrow arena contents

+ 1 - 1
Cargo.toml

@@ -3,7 +3,6 @@ members = [
     # Built-in
     "packages/dioxus",
     "packages/core",
-    "packages/hooks",
     "packages/recoil",
     "packages/redux",
     "packages/core-macro",
@@ -15,6 +14,7 @@ members = [
     # "packages/macro",
     # TODO @Jon, share the validation code
     # "packages/web",
+    # "packages/hooks",
     "packages/cli",
     "examples",
     "packages/html-macro",

+ 0 - 2
packages/core/examples/contextapi.rs

@@ -19,12 +19,10 @@ static Example: FC<Props> = |ctx, props| {
     ctx.view(move |bump| {
         button(bump)
             .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);
             })

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

@@ -4,131 +4,21 @@ use dioxus_core::prelude::*;
 
 fn main() {}
 
-static Example: FC<()> = |ctx, props| {
-    todo!()
-    // let (val1, set_val1) = use_state(&ctx, || "b1");
+/*
+Our flagship demo :)
 
-    // 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>
-    // })
+*/
+static Example: FC<()> = |ctx, props| {
+    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>
+    })
 };
-
-use use_state_def::use_state;
-mod use_state_def {
-    use dioxus_core::prelude::*;
-    use std::{borrow::BorrowMut, cell::RefCell, ops::DerefMut, rc::Rc};
-
-    struct UseState<T: 'static> {
-        new_val: Rc<RefCell<Option<T>>>,
-        current_val: T,
-        caller: Box<dyn Fn(T) + 'static>,
-    }
-
-    /// Store state between component renders!
-    /// When called, this hook retrives a stored value and provides a setter to update that value.
-    /// When the setter is called, the component is re-ran with the new value.
-    ///
-    /// This is behaves almost exactly the same way as React's "use_state".
-    ///
-    /// Usage:
-    /// ```ignore
-    /// static Example: FC<()> = |ctx| {
-    ///     let (counter, set_counter) = use_state(ctx, || 0);
-    ///     let increment = || set_couter(counter + 1);
-    ///     let decrement = || set_couter(counter + 1);
-    ///
-    ///     html! {
-    ///         <div>
-    ///             <h1>"Counter: {counter}" </h1>
-    ///             <button onclick={increment}> "Increment" </button>
-    ///             <button onclick={decrement}> "Decrement" </button>
-    ///         </div>  
-    ///     }
-    /// }
-    /// ```
-    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(
-            move || UseState {
-                new_val: Rc::new(RefCell::new(None)),
-                current_val: initial_state_fn(),
-                caller: Box::new(|_| println!("setter called!")),
-            },
-            move |hook| {
-                let inner = hook.new_val.clone();
-                let scheduled_update = ctx.schedule_update();
-
-                // get ownership of the new val and replace the current with the new
-                // -> as_ref -> borrow_mut -> deref_mut -> take
-                // -> rc     -> &RefCell   -> RefMut    -> &Option<T> -> T
-                if let Some(new_val) = hook.new_val.as_ref().borrow_mut().deref_mut().take() {
-                    hook.current_val = new_val;
-                }
-
-                // todo: swap out the caller with a subscription call and an internal update
-                hook.caller = Box::new(move |new_val| {
-                    // update the setter with the new value
-                    let mut new_inner = inner.as_ref().borrow_mut();
-                    *new_inner = Some(new_val);
-
-                    // Ensure the component gets updated
-                    scheduled_update();
-                });
-
-                // box gets derefed into a ref which is then taken as ref with the hook
-                (&hook.current_val, &hook.caller)
-            },
-            |_| {},
-        )
-    }
-}
-
-mod use_ref_def {
-    use dioxus_core::prelude::*;
-    use std::{borrow::BorrowMut, cell::RefCell, ops::DerefMut, rc::Rc};
-
-    pub struct UseRef<T: 'static> {
-        current: RefCell<T>,
-    }
-    impl<T: 'static> UseRef<T> {
-        fn new(val: T) -> Self {
-            Self {
-                current: RefCell::new(val),
-            }
-        }
-
-        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);
-        }
-    }
-
-    /// Store a mutable value between renders!
-    /// 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, 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, |_| {})
-    }
-}

+ 40 - 0
packages/core/examples/nested.rs

@@ -0,0 +1,40 @@
+#![allow(unused)]
+//! Example of components in
+
+use std::borrow::Borrow;
+
+use dioxus_core::prelude::*;
+
+fn main() {}
+
+static Header: FC<()> = |ctx, props| {
+    let inner = use_ref(&ctx, || 0);
+
+    let handler1 = move || println!("Value is {}", inner.current());
+
+    ctx.view(html! {
+        <div>
+            <h1> "This is the header bar" </h1>
+            <h1> "Idnt it awesome" </h1>
+            <button onclick={move |_| handler1()}> "Click me" </button>
+        </div>
+    })
+};
+
+static Bottom: FC<()> = |ctx, props| {
+    ctx.view(html! {
+        <div>
+            <h1> "bruh 1" </h1>
+            <h1> "bruh 2" </h1>
+        </div>
+    })
+};
+
+static Example: FC<()> = |ctx, props| {
+    ctx.view(html! {
+        <div>
+            <h1> "BROSKI!" </h1>
+            <h1> "DRO!" </h1>
+        </div>
+    })
+};

+ 9 - 4
packages/core/examples/step.rs

@@ -20,15 +20,20 @@ struct Props {
     name: String,
 }
 impl Properties for Props {
-    fn new() -> Self {
-        todo!()
-    }
+    fn call(&self, ptr: *const ()) {}
+
+    // fn new() -> Self {
+    //     todo!()
+    // }
 }
 
 static Example: FC<Props> = |ctx, props| {
     ctx.view(html! {
         <div>
-            <h1> </h1>
+            <h1> "hello world!" </h1>
+            <h1> "hello world!" </h1>
+            <h1> "hello world!" </h1>
+            <h1> "hello world!" </h1>
         </div>
     })
 };

+ 3 - 5
packages/core/src/component.rs

@@ -23,15 +23,13 @@ impl<P: Properties> Component for FC<P> {
 
 /// 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: 'static {
-    fn new() -> Self;
+pub trait Properties {
+    fn call(&self, ptr: *const ()) {}
 }
 
 // Auto implement for no-prop components
 impl Properties for () {
-    fn new() -> Self {
-        ()
-    }
+    fn call(&self, ptr: *const ()) {}
 }
 
 #[cfg(test)]

+ 1 - 1
packages/core/src/context.rs

@@ -27,7 +27,7 @@ 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> {
-    // pub struct Context<'src, PropType> {
+    pub(crate) scope: &'src Scope,
     /// Direct access to the properties used to create this component.
     // pub props: &'src PropType,
     pub idx: AtomicUsize,

+ 116 - 0
packages/core/src/hooks.rs

@@ -0,0 +1,116 @@
+//! Useful, foundational hooks that 3rd parties can implement.
+//! Currently implemented:
+//! - use_ref
+//! - use_state
+
+pub use use_ref_def::use_ref;
+pub use use_state_def::use_state;
+
+mod use_state_def {
+    use crate::inner::*;
+    use std::{borrow::BorrowMut, cell::RefCell, ops::DerefMut, rc::Rc};
+
+    struct UseState<T: 'static> {
+        new_val: Rc<RefCell<Option<T>>>,
+        current_val: T,
+        caller: Box<dyn Fn(T) + 'static>,
+    }
+
+    /// Store state between component renders!
+    /// When called, this hook retrives a stored value and provides a setter to update that value.
+    /// When the setter is called, the component is re-ran with the new value.
+    ///
+    /// This is behaves almost exactly the same way as React's "use_state".
+    ///
+    /// Usage:
+    /// ```ignore
+    /// static Example: FC<()> = |ctx| {
+    ///     let (counter, set_counter) = use_state(ctx, || 0);
+    ///     let increment = || set_couter(counter + 1);
+    ///     let decrement = || set_couter(counter + 1);
+    ///
+    ///     html! {
+    ///         <div>
+    ///             <h1>"Counter: {counter}" </h1>
+    ///             <button onclick={increment}> "Increment" </button>
+    ///             <button onclick={decrement}> "Decrement" </button>
+    ///         </div>  
+    ///     }
+    /// }
+    /// ```
+    pub fn use_state<'a, T: 'static, F: FnOnce() -> T + 'static>(
+        ctx: &'_ Context<'a>,
+        initial_state_fn: F,
+    ) -> (&'a T, &'a impl Fn(T)) {
+        ctx.use_hook(
+            move || UseState {
+                new_val: Rc::new(RefCell::new(None)),
+                current_val: initial_state_fn(),
+                caller: Box::new(|_| println!("setter called!")),
+            },
+            move |hook| {
+                let inner = hook.new_val.clone();
+                let scheduled_update = ctx.schedule_update();
+
+                // get ownership of the new val and replace the current with the new
+                // -> as_ref -> borrow_mut -> deref_mut -> take
+                // -> rc     -> &RefCell   -> RefMut    -> &Option<T> -> T
+                if let Some(new_val) = hook.new_val.as_ref().borrow_mut().deref_mut().take() {
+                    hook.current_val = new_val;
+                }
+
+                // todo: swap out the caller with a subscription call and an internal update
+                hook.caller = Box::new(move |new_val| {
+                    // update the setter with the new value
+                    let mut new_inner = inner.as_ref().borrow_mut();
+                    *new_inner = Some(new_val);
+
+                    // Ensure the component gets updated
+                    scheduled_update();
+                });
+
+                // box gets derefed into a ref which is then taken as ref with the hook
+                (&hook.current_val, &hook.caller)
+            },
+            |_| {},
+        )
+    }
+}
+
+mod use_ref_def {
+    use crate::inner::*;
+    use std::{borrow::BorrowMut, cell::RefCell, ops::DerefMut, rc::Rc};
+
+    pub struct UseRef<T: 'static> {
+        _current: RefCell<T>,
+    }
+    impl<T: 'static> UseRef<T> {
+        fn new(val: T) -> Self {
+            Self {
+                _current: RefCell::new(val),
+            }
+        }
+
+        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);
+        }
+
+        pub fn current(&self) -> std::cell::Ref<'_, T> {
+            self._current.borrow()
+        }
+    }
+
+    /// Store a mutable value between renders!
+    /// To read the value, borrow the ref.
+    /// To change it, use modify.
+    /// Modifications to this value do not cause updates to the component
+    /// Attach to inner context reference, so context can be consumed
+    pub fn use_ref<'a, T: 'static>(
+        ctx: &'_ Context<'a>,
+        initial_state_fn: impl FnOnce() -> T + 'static,
+    ) -> &'a UseRef<T> {
+        ctx.use_hook(|| UseRef::new(initial_state_fn()), |state| &*state, |_| {})
+    }
+}

+ 3 - 0
packages/core/src/lib.rs

@@ -69,6 +69,7 @@ pub mod component;
 pub mod context;
 pub mod debug_renderer;
 pub mod events;
+pub mod hooks;
 pub mod nodebuilder;
 pub mod nodes;
 pub mod scope;
@@ -131,4 +132,6 @@ pub mod prelude {
     pub use crate::nodebuilder as builder;
     pub use dioxus_core_macro::fc;
     pub use dioxus_html_2::html;
+
+    pub use crate::hooks::*;
 }

+ 22 - 12
packages/core/src/nodes.rs

@@ -37,7 +37,7 @@ mod vnode {
         Suspended,
 
         /// A User-defined componen node (node type COMPONENT_NODE)
-        Component(VComponent),
+        Component(VComponent<'src>),
     }
 
     impl<'a> VNode<'a> {
@@ -174,11 +174,7 @@ mod velement {
     }
 
     /// An event listener.
-    pub struct Listener<'a>
-// pub struct Listener<'a, 'b>
-    // where
-    //     'b: 'a + 'static,
-    {
+    pub struct Listener<'a> {
         /// The type of event to listen for.
         pub(crate) event: &'a str,
         /// The callback to invoke when the event happens.
@@ -249,19 +245,33 @@ mod vtext {
 /// Only supports the functional syntax
 mod vcomponent {
     use crate::prelude::Properties;
-    use std::{any::TypeId, fmt, future::Future};
+    use std::{any::TypeId, fmt, future::Future, marker::PhantomData};
 
     use super::VNode;
-    #[derive(PartialEq)]
-    pub struct VComponent {
-        // props_id: TypeId,
+
+    pub struct VComponent<'src> {
+        _p: PhantomData<&'src ()>,
+        runner: Box<dyn Properties + 'src>,
+        caller: *const (),
+        props_type: TypeId,
+    }
+
+    // impl<'src> PartialEq for CallerSource<'src> {
+    //     fn eq(&self, other: &Self) -> bool {
+    //         todo!()
+    //     }
+    // }
+    // caller: Box<dyn Fn
+    // should we box the caller?
+    // probably, so we can call it again
+    //
+    // props_id: TypeId,
     // callerIDs are unsafely coerced to function pointers
     // This is okay because #1, we store the props_id and verify and 2# the html! macro rejects components not made this way
     //
     // Manually constructing the VComponent is not possible from 3rd party crates
-    }
 
-    impl VComponent {
+    impl<'a> VComponent<'a> {
         // /// Construct a VComponent directly from a function component
         // /// This should be *very* fast - we store the function pointer and props type ID. It should also be small on the stack
         // pub fn from_fn<P: Properties>(f: FC<P>, props: P) -> Self {

+ 7 - 6
packages/core/src/scope.rs

@@ -47,18 +47,19 @@ impl Scope {
         }
     }
 
-    pub fn create_context<'runner, T: Properties>(
-        &'runner mut self,
-        components: &'runner generational_arena::Arena<Scope>,
-        props: &'runner T,
+    pub fn create_context<'a, T: Properties>(
+        &'a mut self,
+        components: &'a generational_arena::Arena<Scope>,
+        props: &'a T,
     ) -> Context {
-        // ) -> Context<T> {
+        
+        //
         Context {
+            scope: &*self,
             _p: PhantomData {},
             arena: &self.hook_arena,
             hooks: &self.hooks,
             idx: 0.into(),
-            // props,
             components,
         }
     }

+ 0 - 1
packages/dioxus/src/lib.rs

@@ -1,7 +1,6 @@
 pub mod prelude {
     pub use dioxus_core::prelude::*;
     pub use dioxus_core_macro::fc;
-    pub use dioxus_hooks::prelude::use_state;
 }
 
 use dioxus_core::prelude::FC;

+ 7 - 7
packages/hooks/src/lib.rs

@@ -1,10 +1,10 @@
 // mod hooks;
 // pub use hooks::use_context;
 
-pub mod prelude {
-    use dioxus_core::prelude::Context;
-    pub fn use_state<T, G>(ctx: &Context<G>, init: impl Fn() -> T) -> (T, impl Fn(T)) {
-        let g = init();
-        (g, |_| {})
-    }
-}
+// pub mod prelude {
+//     use dioxus_core::prelude::Context;
+//     pub fn use_state<T, G>(ctx: &Context<G>, init: impl Fn() -> T) -> (T, impl Fn(T)) {
+//         let g = init();
+//         (g, |_| {})
+//     }
+// }

+ 6 - 10
packages/html-macro-2/src/lib.rs

@@ -1,7 +1,6 @@
 use ::{
     proc_macro::TokenStream,
     proc_macro2::{Span, TokenStream as TokenStream2},
-    proc_macro_hack::proc_macro_hack,
     quote::{quote, ToTokens, TokenStreamExt},
     style_shared::Styles,
     syn::{
@@ -77,7 +76,6 @@ struct NodeList(Vec<MaybeExpr<Node>>);
 
 impl ToTokens for ToToksCtx<&NodeList> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
-        // let ctx = &self.ctx;
         let nodes = self.inner.0.iter().map(|node| self.recurse(node));
         tokens.append_all(quote! {
             dioxus::bumpalo::vec![in bump;
@@ -226,12 +224,17 @@ impl Parse for Attr {
         let mut name = Ident::parse_any(s)?;
         let name_str = name.to_string();
         s.parse::<Token![=]>()?;
+
+        // Check if this is an event handler
+        // If so, parse into literal tokens
         let ty = if name_str.starts_with("on") {
             // remove the "on" bit
             name = Ident::new(&name_str.trim_start_matches("on"), name.span());
             let content;
             syn::braced!(content in s);
+            // AttrType::Value(content.parse()?)
             AttrType::Event(content.parse()?)
+        // AttrType::Event(content.parse()?)
         } else {
             let lit_str = if name_str == "style" && s.peek(token::Brace) {
                 // special-case to deal with literal styles.
@@ -332,23 +335,16 @@ where
 
 /// ToTokens context
 struct ToToksCtx<T> {
-    // struct ToToksCtx<'a, T> {
     inner: T,
-    // ctx: &'a Ident,
 }
 
 impl<'a, T> ToToksCtx<T> {
     fn new(inner: T) -> Self {
-        // fn new(ctx: &'a Ident, inner: T) -> Self {
         ToToksCtx { inner }
     }
 
     fn recurse<U>(&self, inner: U) -> ToToksCtx<U> {
-        // fn recurse<U>(&self, inner: U) -> ToToksCtx<'a, U> {
-        ToToksCtx {
-            // ctx: &self.ctx,
-            inner,
-        }
+        ToToksCtx { inner }
     }
 }