Jelajahi Sumber

wip: some project refactor

Jonathan Kelley 4 tahun lalu
induk
melakukan
8cfc437

+ 2 - 1
Cargo.toml

@@ -13,7 +13,7 @@ dioxus-core-macro = { path="./packages/core-macro", version="0.1.0" }
 dioxus-html = { path="./packages/html", optional=true }
 dioxus-web = { path="./packages/web", optional=true }
 dioxus-webview = { path="./packages/webview", optional=true }
-# dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
+dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
 
 
 [features]
@@ -49,6 +49,7 @@ members = [
     "packages/html",
     "packages/web",
     "packages/webview",
+    "packages/hooks",
     # "packages/atoms",
     # "packages/ssr",
     # "packages/docsite",

+ 4 - 3
packages/core/examples/async.rs

@@ -6,16 +6,17 @@ use futures::Future;
 fn main() {}
 
 const App: FC<()> = |cx| {
+    // create a new future
     let mut fut = cx.use_hook(
         || {
             //
-            Box::pin(async { loop {} }) as Pin<Box<dyn Future<Output = ()>>>
+            async { loop {} }
+            // Box::pin(async { loop {} }) as Pin<Box<dyn Future<Output = ()>>>
         },
         |f| f,
         |_| {},
     );
-
-    cx.submit_task(fut);
+    // let g = unsafe { Pin::new_unchecked(fut) };
 
     cx.submit_task(fut);
 

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

@@ -20,7 +20,7 @@ struct ListItem {
 }
 
 fn app(cx: Context<AppProps>) -> VNode {
-    let (val, set_val) = use_state_classic(cx, || 0);
+    // let (val, set_val) = use_state_classic(cx, || 0);
 
     cx.render(LazyNodes::new(move |_nodecx| {
         todo!()

+ 11 - 15
packages/core/src/arena.rs

@@ -2,37 +2,33 @@ use std::{
     cell::{RefCell, UnsafeCell},
     collections::HashMap,
     rc::Rc,
+    sync::Arc,
 };
 
 use crate::innerlude::*;
 use slotmap::{DefaultKey, SlotMap};
 
 #[derive(Clone)]
-pub struct ScopeArena(pub Rc<RefCell<ScopeArenaInner>>);
-pub type ScopeMap = SlotMap<DefaultKey, Scope>;
-
-pub struct ScopeArenaInner {
-    pub(crate) arena: UnsafeCell<ScopeMap>,
-    locks: HashMap<ScopeIdx, MutStatus>,
+pub struct SharedArena {
+    pub components: Arc<UnsafeCell<ScopeMap>>,
 }
+pub type ScopeMap = SlotMap<DefaultKey, Scope>;
 
 enum MutStatus {
     Immut,
     Mut,
 }
 
-impl ScopeArena {
+impl SharedArena {
     pub fn new(arena: ScopeMap) -> Self {
-        ScopeArena(Rc::new(RefCell::new(ScopeArenaInner {
-            arena: UnsafeCell::new(arena),
-            locks: Default::default(),
-        })))
+        let components = Arc::new(UnsafeCell::new(arena));
+        SharedArena { components }
     }
 
     /// THIS METHOD IS CURRENTLY UNSAFE
     /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
     pub fn try_get(&self, idx: ScopeIdx) -> Result<&Scope> {
-        let inner = unsafe { &*self.0.borrow().arena.get() };
+        let inner = unsafe { &*self.components.get() };
         let scope = inner.get(idx);
         scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
     }
@@ -40,7 +36,7 @@ impl ScopeArena {
     /// THIS METHOD IS CURRENTLY UNSAFE
     /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
     pub fn try_get_mut(&self, idx: ScopeIdx) -> Result<&mut Scope> {
-        let inner = unsafe { &mut *self.0.borrow().arena.get() };
+        let inner = unsafe { &mut *self.components.get() };
         let scope = inner.get_mut(idx);
         scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
     }
@@ -56,7 +52,7 @@ impl ScopeArena {
     /// THIS METHOD IS CURRENTLY UNSAFE
     /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
     pub fn with<T>(&self, f: impl FnOnce(&mut ScopeMap) -> T) -> Result<T> {
-        let inner = unsafe { &mut *self.0.borrow().arena.get() };
+        let inner = unsafe { &mut *self.components.get() };
         Ok(f(inner))
         // todo!()
     }
@@ -80,7 +76,7 @@ impl ScopeArena {
     }
 
     pub fn try_remove(&self, id: ScopeIdx) -> Result<Scope> {
-        let inner = unsafe { &mut *self.0.borrow().arena.get() };
+        let inner = unsafe { &mut *self.components.get() };
         inner
             .remove(id)
             .ok_or_else(|| Error::FatalInternal("Scope not found"))

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

@@ -1,5 +1,5 @@
 use crate::hooklist::HookList;
-use crate::{arena::ScopeArena, innerlude::*};
+use crate::{arena::SharedArena, innerlude::*};
 use appendlist::AppendList;
 use bumpalo::Bump;
 use futures::FutureExt;

+ 0 - 1
packages/core/src/component.rs

@@ -43,7 +43,6 @@ pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
 }
 
 /// Create inline fragments
-/// ------------------------
 ///
 /// Fragments capture a series of children without rendering extra nodes.
 ///

+ 10 - 6
packages/core/src/context.rs

@@ -1,5 +1,5 @@
 use crate::hooklist::HookList;
-use crate::{arena::ScopeArena, innerlude::*};
+use crate::{arena::SharedArena, innerlude::*};
 use appendlist::AppendList;
 use bumpalo::Bump;
 use futures::FutureExt;
@@ -40,10 +40,11 @@ use std::{
 pub struct Context<'src, T> {
     pub props: &'src T,
     pub scope: &'src Scope,
-    pub tasks: &'src AppendList<&'src mut DTask>,
+    pub tasks: &'src AppendList<&'src mut DTask<'src>>,
 }
 
-pub type DTask = Pin<Box<dyn Future<Output = ()>>>;
+pub type DTask<'s> = dyn Future<Output = ()> + 's;
+// pub type DTask = Pin<Box<dyn Future<Output = ()>>>;
 
 impl<'src, T> Copy for Context<'src, T> {}
 impl<'src, T> Clone for Context<'src, T> {
@@ -293,12 +294,15 @@ Any function prefixed with "use" should not be called conditionally.
     ///
     /// Tasks can't return anything, but they can be controlled with the returned handle
     ///
-    /// Tasks will only run until the component renders again. If you want your task to last longer than one frame, you'll need
-    /// to store it somehow.
+    /// Tasks will only run until the component renders again. Because `submit_task` is valid for the &'src lifetime, it
+    /// is considered "stable"
+    ///
+    ///
     ///
     pub fn submit_task(
         &self,
-        mut task: &'src mut Pin<Box<dyn Future<Output = ()> + 'static>>,
+        mut task: &'src mut DTask<'src>,
+        // mut task: &'src mut Pin<Box<dyn Future<Output = ()> + 'static>>,
     ) -> TaskHandle {
         self.tasks.push(task);
         // let mut g = self.task.borrow_mut();

+ 7 - 10
packages/core/src/diff.rs

@@ -22,10 +22,7 @@
 //! More info on how to improve this diffing algorithm:
 //!  - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
 
-use crate::{
-    arena::{ScopeArena, ScopeArenaInner},
-    innerlude::*,
-};
+use crate::{arena::SharedArena, innerlude::*};
 use fxhash::{FxHashMap, FxHashSet};
 
 use std::{
@@ -96,7 +93,7 @@ pub trait RealDom<'a> {
 /// subscriptions and props changes.
 pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
     pub dom: &'real mut Dom,
-    pub components: &'bump ScopeArena,
+    pub components: &'bump SharedArena,
     pub cur_idx: ScopeIdx,
     pub diffed: FxHashSet<ScopeIdx>,
     pub event_queue: EventQueue,
@@ -106,7 +103,7 @@ pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
 impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
     pub fn new(
         dom: &'real mut Dom,
-        components: &'bump ScopeArena,
+        components: &'bump SharedArena,
         cur_idx: ScopeIdx,
         event_queue: EventQueue,
     ) -> Self {
@@ -376,7 +373,7 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
                 // self.dom.save_known_root(root_id);
 
                 log::debug!("Mounting a new component");
-                let caller: Weak<OpaqueComponent> = Rc::downgrade(&component.caller);
+                let caller: Weak<WrappedCaller> = Rc::downgrade(&component.caller);
 
                 // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children
                 // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
@@ -413,7 +410,7 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
 
                 // yaaaaay lifetimes out of thin air
                 // really tho, we're merging the frame lifetimes together
-                let inner: &'bump mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
+                let inner: &'bump mut _ = unsafe { &mut *self.components.components.get() };
                 let new_component = inner.get_mut(idx).unwrap();
 
                 // Actually initialize the caller's slot with the right address
@@ -1233,7 +1230,7 @@ enum KeyedPrefixResult {
 /// This iterator is useful when it's important to load the next real root onto the top of the stack for operations like
 /// "InsertBefore".
 struct RealChildIterator<'a> {
-    scopes: &'a ScopeArena,
+    scopes: &'a SharedArena,
 
     // Heuristcally we should never bleed into 5 completely nested fragments/components
     // Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane
@@ -1241,7 +1238,7 @@ struct RealChildIterator<'a> {
 }
 
 impl<'a> RealChildIterator<'a> {
-    fn new(starter: &'a VNode<'a>, scopes: &'a ScopeArena) -> Self {
+    fn new(starter: &'a VNode<'a>, scopes: &'a SharedArena) -> Self {
         Self {
             scopes,
             stack: smallvec::smallvec![(0, starter)],

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

@@ -6,7 +6,7 @@
 
 use std::{ops::Deref, rc::Rc};
 
-use crate::{innerlude::ScopeIdx, virtual_dom::RealDomNode};
+use crate::innerlude::{RealDomNode, ScopeIdx};
 
 #[derive(Debug)]
 pub struct EventTrigger {

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

@@ -1,413 +0,0 @@
-//! Useful, foundational hooks that 3rd parties can implement.
-//! Currently implemented:
-//! - [x] use_ref
-//! - [x] use_state
-//! - [ ] use_reducer
-//! - [ ] use_effect
-
-use crate::innerlude::Context;
-
-use std::{
-    cell::{Cell, RefCell},
-    ops::{Deref, DerefMut},
-    rc::Rc,
-};
-
-/// 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<()> = |cx| {
-///     let (counter, set_counter) = use_state(cx, || 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_classic<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
-    cx: Context<'a, P>,
-    initial_state_fn: F,
-) -> (&'a T, &'a Rc<dyn Fn(T)>) {
-    struct UseState<T: 'static> {
-        new_val: Rc<RefCell<Option<T>>>,
-        current_val: T,
-        caller: Rc<dyn Fn(T) + 'static>,
-    }
-
-    cx.use_hook(
-        move || UseState {
-            new_val: Rc::new(RefCell::new(None)),
-            current_val: initial_state_fn(),
-            caller: Rc::new(|_| println!("setter called!")),
-        },
-        move |hook| {
-            log::debug!("Use_state set called");
-            let inner = hook.new_val.clone();
-            let scheduled_update = cx.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 = Rc::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)
-        },
-        |_| {},
-    )
-}
-
-use crate::innerlude::*;
-use std::{
-    fmt::{Debug, Display},
-    pin::Pin,
-};
-
-pub struct UseState<'a, T: 'static> {
-    inner: &'a UseStateInner<T>,
-}
-impl<T> Copy for UseState<'_, T> {}
-impl<'a, T> Clone for UseState<'a, T>
-where
-    T: 'static,
-{
-    fn clone(&self) -> Self {
-        UseState { inner: self.inner }
-    }
-}
-
-impl<'a, T: 'static> UseState<'a, T> {
-    /// Tell the Dioxus Scheduler that we need to be processed
-    pub fn needs_update(&self) {
-        if !self.inner.update_scheuled.get() {
-            self.inner.update_scheuled.set(true);
-            (self.inner.callback)();
-        }
-    }
-
-    pub fn set(&self, new_val: T) {
-        self.needs_update();
-        *self.inner.wip.borrow_mut() = Some(new_val);
-    }
-
-    pub fn get(&self) -> &T {
-        &self.inner.current_val
-    }
-
-    /// Get the current status of the work-in-progress data
-    pub fn get_wip(&self) -> Ref<Option<T>> {
-        self.inner.wip.borrow()
-    }
-}
-impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
-    pub fn get_mut(self) -> RefMut<'a, T> {
-        // make sure we get processed
-        self.needs_update();
-
-        // Bring out the new value, cloning if it we need to
-        // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
-        RefMut::map(self.inner.wip.borrow_mut(), |slot| {
-            if slot.is_none() {
-                *slot = Some(self.inner.current_val.to_owned());
-            }
-            slot.as_mut().unwrap()
-        })
-    }
-}
-
-impl<'a, T: 'static> std::ops::Deref for UseState<'a, T> {
-    type Target = T;
-
-    fn deref(&self) -> &Self::Target {
-        &self.inner.current_val
-    }
-}
-
-use std::ops::{Add, AddAssign, Sub, SubAssign};
-impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseState<'a, T> {
-    type Output = T;
-
-    fn add(self, rhs: T) -> Self::Output {
-        self.inner.current_val.add(rhs)
-    }
-}
-impl<'a, T: Copy + Add<T, Output = T>> AddAssign<T> for UseState<'a, T> {
-    fn add_assign(&mut self, rhs: T) {
-        self.set(self.inner.current_val.add(rhs));
-    }
-}
-impl<'a, T: Copy + Sub<T, Output = T>> Sub<T> for UseState<'a, T> {
-    type Output = T;
-
-    fn sub(self, rhs: T) -> Self::Output {
-        self.inner.current_val.sub(rhs)
-    }
-}
-impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseState<'a, T> {
-    fn sub_assign(&mut self, rhs: T) {
-        self.set(self.inner.current_val.sub(rhs));
-    }
-}
-
-// enable displaty for the handle
-impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.inner.current_val)
-    }
-}
-struct UseStateInner<T: 'static> {
-    current_val: T,
-    update_scheuled: Cell<bool>,
-    callback: Rc<dyn Fn()>,
-    wip: RefCell<Option<T>>,
-}
-
-/// Store state between component renders!
-///
-/// ## The "King" of state hooks
-///
-/// The Dioxus version of `useState` is the "king daddy" of state management. It allows you to ergonomically store and
-/// modify state between component renders. When the state is updated, the component will re-render.
-///
-/// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
-///
-/// [`use_state`] exposes a few helper methods to modify the underlying state:
-/// - `.set(new)` allows you to override the "work in progress" value with a new value
-/// - `.get_mut()` allows you to modify the WIP value
-/// - `.get_wip()` allows you to access the WIP value
-/// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required)
-///
-/// Additionally, a ton of std::ops traits are implemented for the `UseState` wrapper, meaning any mutative type operations
-/// will automatically be called on the WIP value.
-///
-///
-/// Usage:
-/// ```ignore
-/// const Example: FC<()> = |cx| {
-///     let counter = use_state(cx, || 0);
-///     let increment = |_| counter += 1;
-///     let decrement = |_| counter += 1;
-///
-///     html! {
-///         <div>
-///             <h1>"Counter: {counter}" </h1>
-///             <button onclick={increment}> "Increment" </button>
-///             <button onclick={decrement}> "Decrement" </button>
-///         </div>  
-///     }
-/// }
-/// ```
-pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
-    cx: Context<'a, P>,
-    initial_state_fn: F,
-) -> UseState<T> {
-    cx.use_hook(
-        move || UseStateInner {
-            current_val: initial_state_fn(),
-            callback: cx.schedule_update(),
-            wip: RefCell::new(None),
-            update_scheuled: Cell::new(false),
-        },
-        move |hook| {
-            hook.update_scheuled.set(false);
-            let mut new_val = hook.wip.borrow_mut();
-            if new_val.is_some() {
-                hook.current_val = new_val.take().unwrap();
-            }
-
-            UseState { inner: &*hook }
-        },
-        |_| {},
-    )
-}
-
-/// 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, P>(
-    cx: Context<'a, P>,
-    initial_state_fn: impl FnOnce() -> T + 'static,
-) -> &'a RefCell<T> {
-    cx.use_hook(|| RefCell::new(initial_state_fn()), |state| &*state, |_| {})
-}
-
-struct UseReducer<T: 'static, R: 'static> {
-    new_val: Rc<RefCell<Option<T>>>,
-    current_val: T,
-    caller: Box<dyn Fn(R) + '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".
-///
-pub fn use_reducer<'a, 'c, State: 'static, Action: 'static, P>(
-    cx: Context<'a, P>,
-    initial_state_fn: impl FnOnce() -> State,
-    _reducer: impl Fn(&mut State, Action),
-) -> (&'a State, &'a Box<dyn Fn(Action)>) {
-    cx.use_hook(
-        move || UseReducer {
-            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 = cx.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)
-        },
-        |_| {},
-    )
-}
-
-/// Use model makes it easy to use "models" as state for components. To modify the model, call "modify" and a clone of the
-/// current model will be made, with a RefMut lock on it. Dioxus will never run your components multithreaded, so you can
-/// be relatively sure that this won't fail in practice
-pub fn use_model<'a, T: ToOwned<Owned = T> + 'static, P>(
-    cx: Context<'a, P>,
-    f: impl FnOnce() -> T,
-) -> &'a UseModel<T> {
-    cx.use_hook(
-        move || {
-            let real = f();
-            let wip = RefCell::new(real.to_owned());
-            let update = cx.schedule_update();
-            UseModel { real, wip, update }
-        },
-        |hook| {
-            hook.real = hook.wip.borrow().to_owned();
-            &*hook
-        },
-        |_| {},
-    )
-}
-
-pub struct UseModel<T: ToOwned> {
-    real: T,
-    wip: RefCell<T>,
-    update: Rc<dyn Fn()>,
-}
-
-use std::cell::{Ref, RefMut};
-impl<T: ToOwned> Deref for UseModel<T> {
-    type Target = T;
-
-    fn deref(&self) -> &Self::Target {
-        &self.real
-    }
-}
-
-impl<T: Display + ToOwned> Display for UseModel<T> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.real)
-    }
-}
-
-impl<T: ToOwned> UseModel<T> {
-    pub fn get_mut(&self) -> RefMut<'_, T> {
-        (self.update)();
-        self.wip.borrow_mut()
-    }
-    pub fn modify(&self, f: impl FnOnce(&mut T)) {
-        (self.update)();
-        let mut g = self.get_mut();
-        let r = g.deref_mut();
-        f(r)
-    }
-}
-
-// #[cfg(test)]
-mod tests {
-
-    use crate::prelude::*;
-
-    enum Actions {
-        Incr,
-        Decr,
-    }
-
-    // #[allow(unused)]
-    // static Example: FC<()> = |cx| {
-    //     let (count, reduce) = use_reducer(
-    //         &cx,
-    //         || 0,
-    //         |count, action| match action {
-    //             Actions::Incr => *count += 1,
-    //             Actions::Decr => *count -= 1,
-    //         },
-    //     );
-
-    //     cx.render(rsx! {
-    //         div {
-    //             h1 {"Count: {count}"}
-    //             button {
-    //                 "Increment"
-    //                 onclick: move |_| reduce(Actions::Incr)
-    //             }
-    //             button {
-    //                 "Decrement"
-    //                 onclick: move |_| reduce(Actions::Decr)
-    //             }
-    //         }
-    //     })
-    // };
-}
-
-pub fn use_is_initialized<'a, P>(cx: Context<'a, P>) -> bool {
-    let val = use_ref(cx, || false);
-    match *val.borrow() {
-        true => true,
-        false => {
-            //
-            *val.borrow_mut() = true;
-            false
-        }
-    }
-}

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

@@ -19,7 +19,6 @@ pub mod diff;
 pub mod error;
 pub mod events;
 pub mod hooklist;
-pub mod hooks;
 pub mod nodebuilder;
 pub mod nodes;
 pub mod scope;
@@ -41,7 +40,6 @@ pub(crate) mod innerlude {
     pub use crate::diff::*;
     pub use crate::error::*;
     pub use crate::events::*;
-    pub use crate::hooks::*;
     pub use crate::nodebuilder::*;
     pub use crate::nodes::*;
     pub use crate::scope::*;
@@ -84,7 +82,4 @@ pub mod prelude {
 
     pub use crate::diff::DiffMachine;
     pub use crate::virtual_dom::ScopeIdx;
-
-    // pub use crate::debug_renderer::DebugRenderer;
-    pub use crate::hooks::*;
 }

+ 5 - 5
packages/core/src/nodebuilder.rs

@@ -14,10 +14,9 @@ use bumpalo::Bump;
 
 use crate::{
     events::VirtualEvent,
-    innerlude::{Properties, Scope, VComponent, VText, FC},
+    innerlude::{Properties, RealDomNode, Scope, VComponent, VText, FC},
     nodes::{Attribute, Listener, NodeKey, VNode},
     prelude::{VElement, VFragment},
-    virtual_dom::RealDomNode,
 };
 
 /// A virtual DOM element builder.
@@ -509,6 +508,7 @@ where
     ///     .finish();
     /// ```    
     pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
+        todo!();
         let len_before = self.children.len();
         for item in nodes {
             todo!()
@@ -542,13 +542,13 @@ impl<'a> IntoIterator for VNode<'a> {
     }
 }
 impl<'a> IntoVNode<'a> for VNode<'a> {
-    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
+    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
         self
     }
 }
 
 impl<'a> IntoVNode<'a> for &VNode<'a> {
-    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
+    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
         self.clone()
     }
 }
@@ -801,7 +801,7 @@ impl<'a> NodeFactory<'a> {
 
 use std::fmt::Debug;
 impl Debug for NodeFactory<'_> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         Ok(())
     }
 }

+ 3 - 4
packages/core/src/nodes.rs

@@ -4,11 +4,10 @@
 //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
 
 use crate::{
-    arena::ScopeArena,
+    arena::SharedArena,
     events::VirtualEvent,
-    innerlude::{Context, Properties, Scope, ScopeIdx, FC},
+    innerlude::{Context, Properties, RealDom, RealDomNode, Scope, ScopeIdx, FC},
     nodebuilder::{text3, NodeFactory},
-    virtual_dom::RealDomNode,
 };
 use bumpalo::Bump;
 use std::{
@@ -165,7 +164,7 @@ impl<'a> VNode<'a> {
         }
     }
 
-    pub fn get_mounted_id(&self, components: &ScopeArena) -> Option<RealDomNode> {
+    pub fn get_mounted_id(&self, components: &SharedArena) -> Option<RealDomNode> {
         match self {
             VNode::Element(el) => Some(el.dom_id.get()),
             VNode::Text(te) => Some(te.dom_id.get()),

+ 17 - 51
packages/core/src/scope.rs

@@ -1,5 +1,5 @@
 use crate::hooklist::HookList;
-use crate::{arena::ScopeArena, innerlude::*};
+use crate::{arena::SharedArena, innerlude::*};
 use appendlist::AppendList;
 use bumpalo::Bump;
 use futures::FutureExt;
@@ -17,6 +17,11 @@ use std::{
     rc::{Rc, Weak},
 };
 
+// We need to pin the hook so it doesn't move as we initialize the list of hooks
+type Hook = Box<dyn std::any::Any>;
+type EventChannel = Rc<dyn Fn()>;
+pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> VNode<'b>;
+
 /// Every component in Dioxus is represented by a `Scope`.
 ///
 /// Scopes contain the state for hooks, the component's props, and other lifecycle information.
@@ -35,7 +40,7 @@ pub struct Scope {
 
     // A reference to the list of components.
     // This lets us traverse the component list whenever we need to access our parent or children.
-    pub arena_link: ScopeArena,
+    pub arena_link: SharedArena,
 
     pub shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
 
@@ -46,7 +51,7 @@ pub struct Scope {
 
     pub event_channel: Rc<dyn Fn() + 'static>,
 
-    pub caller: Weak<OpaqueComponent>,
+    pub caller: Weak<WrappedCaller>,
 
     // ==========================
     // slightly unsafe stuff
@@ -71,10 +76,6 @@ pub struct Scope {
     pub(crate) suspended_tasks: Vec<*mut Pin<Box<dyn Future<Output = VNode<'static>>>>>,
 }
 
-// We need to pin the hook so it doesn't move as we initialize the list of hooks
-type Hook = Box<dyn std::any::Any>;
-type EventChannel = Rc<dyn Fn()>;
-
 impl Scope {
     // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
     // we are going to break this lifetime by force in order to save it on ourselves.
@@ -84,12 +85,12 @@ impl Scope {
     // Scopes cannot be made anywhere else except for this file
     // Therefore, their lifetimes are connected exclusively to the virtual dom
     pub fn new<'creator_node>(
-        caller: Weak<OpaqueComponent>,
+        caller: Weak<WrappedCaller>,
         arena_idx: ScopeIdx,
         parent: Option<ScopeIdx>,
         height: u32,
         event_channel: EventChannel,
-        arena_link: ScopeArena,
+        arena_link: SharedArena,
         child_nodes: &'creator_node [VNode<'creator_node>],
     ) -> Self {
         log::debug!(
@@ -98,25 +99,6 @@ impl Scope {
             arena_idx
         );
 
-        // The function to run this scope is actually located in the parent's bump arena.
-        // Every time the parent is updated, that function is invalidated via double-buffering wiping the old frame.
-        // If children try to run this invalid caller, it *will* result in UB.
-        //
-        // During the lifecycle progression process, this caller will need to be updated. Right now,
-        // until formal safety abstractions are implemented, we will just use unsafe to "detach" the caller
-        // lifetime from the bump arena, exposing ourselves to this potential for invalidation. Truthfully,
-        // this is a bit of a hack, but will remain this way until we've figured out a cleaner solution.
-        //
-        // Not the best solution, so TODO on removing this in favor of a dedicated resource abstraction.
-        let caller = unsafe {
-            std::mem::transmute::<
-                Weak<OpaqueComponent>,
-                Weak<OpaqueComponent>,
-                // Weak<OpaqueComponent<'creator_node>>,
-                // Weak<OpaqueComponent<'static>>,
-            >(caller)
-        };
-
         let child_nodes = unsafe { std::mem::transmute(child_nodes) };
 
         Self {
@@ -137,18 +119,8 @@ impl Scope {
         }
     }
 
-    pub fn update_caller<'creator_node>(&mut self, caller: Weak<OpaqueComponent>) {
-        // pub fn update_caller<'creator_node>(&mut self, caller: Weak<OpaqueComponent<'creator_node>>) {
-        let broken_caller = unsafe {
-            std::mem::transmute::<
-                Weak<OpaqueComponent>,
-                Weak<OpaqueComponent>,
-                // Weak<OpaqueComponent<'creator_node>>,
-                // Weak<OpaqueComponent<'static>>,
-            >(caller)
-        };
-
-        self.caller = broken_caller;
+    pub fn update_caller<'creator_node>(&mut self, caller: Weak<WrappedCaller>) {
+        self.caller = caller;
     }
 
     pub fn update_children<'creator_node>(
@@ -159,10 +131,6 @@ impl Scope {
         self.child_nodes = child_nodes;
     }
 
-    /// 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
-    ///
-    /// 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_scope<'sel>(&'sel mut self) -> Result<()> {
         // Cycle to the next frame and then reset it
         // This breaks any latent references, invalidating every pointer referencing into it.
@@ -180,19 +148,17 @@ impl Scope {
             .ok_or(Error::FatalInternal("Failed to get caller"))?;
 
         // Cast the caller ptr from static to one with our own reference
-        let c2: &OpaqueComponent = caller.as_ref();
-        let c3: &OpaqueComponent = unsafe { std::mem::transmute(c2) };
+        let c3: &WrappedCaller = caller.as_ref();
 
-        self.frames.cur_frame_mut().head_node = unsafe { self.own_vnodes(c3) };
+        self.frames.cur_frame_mut().head_node = unsafe { self.call_user_component(c3) };
 
         Ok(())
     }
 
     // this is its own function so we can preciesly control how lifetimes flow
-    unsafe fn own_vnodes<'a>(&'a self, f: &OpaqueComponent) -> VNode<'static> {
-        let new_head: VNode<'a> = f(self);
-        let out: VNode<'static> = std::mem::transmute(new_head);
-        out
+    unsafe fn call_user_component<'a>(&'a self, caller: &WrappedCaller) -> VNode<'static> {
+        let new_head: VNode<'a> = caller(self);
+        std::mem::transmute(new_head)
     }
 
     // A safe wrapper around calling listeners

+ 9 - 0
packages/core/src/serialize.rs

@@ -1,3 +1,12 @@
+//! Serialization
+//! -------------
+//!
+//!
+//!
+//!
+//!
+//!
+
 use crate::prelude::ScopeIdx;
 use serde::{Deserialize, Serialize};
 

+ 8 - 1
packages/core/src/signals.rs

@@ -1 +1,8 @@
-//! Support for jumping out of the diff engine
+//! Signas: Avoid the Diff Engine
+//! -----------------------------
+//!
+//!
+//!
+//!
+//!
+//!

+ 33 - 7
packages/core/src/util.rs

@@ -1,13 +1,15 @@
-use std::{cell::RefCell, rc::Rc};
+use std::{
+    cell::{RefCell, RefMut},
+    rc::Rc,
+    vec::Drain,
+};
 
 use crate::innerlude::*;
 
-// We actually allocate the properties for components in their parent's properties
-// We then expose a handle to use those props for render in the form of "OpaqueComponent"
-pub type OpaqueComponent = dyn for<'b> Fn(&'b Scope) -> VNode<'b>;
-
 #[derive(PartialEq, Debug, Clone, Default)]
-pub struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
+pub struct EventQueue {
+    pub queue: Rc<RefCell<Vec<HeightMarker>>>,
+}
 
 impl EventQueue {
     pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
@@ -15,9 +17,17 @@ impl EventQueue {
         let marker = HeightMarker { height, idx };
         Rc::new(move || {
             log::debug!("channel updated {:#?}", marker);
-            inner.0.as_ref().borrow_mut().push(marker)
+            inner.queue.as_ref().borrow_mut().push(marker)
         })
     }
+
+    pub fn sort_unstable(&self) {
+        self.queue.borrow_mut().sort_unstable()
+    }
+
+    pub fn borrow_mut(&self) -> RefMut<Vec<HeightMarker>> {
+        self.queue.borrow_mut()
+    }
 }
 
 /// A helper type that lets scopes be ordered by their height
@@ -39,6 +49,22 @@ impl PartialOrd for HeightMarker {
     }
 }
 
+/// The `RealDomNode` is an ID handle that corresponds to a foreign DOM node.
+///
+/// "u64" was chosen for two reasons
+/// - 0 cost hashing
+/// - use with slotmap and other versioned slot arenas
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct RealDomNode(pub u64);
+impl RealDomNode {
+    pub fn new(id: u64) -> Self {
+        Self(id)
+    }
+    pub fn empty() -> Self {
+        Self(u64::MIN)
+    }
+}
+
 pub struct DebugDom {
     counter: u64,
 }

+ 7 - 33
packages/core/src/virtual_dom.rs

@@ -20,7 +20,7 @@
 //! Additional functionality is defined in the respective files.
 
 use crate::tasks::TaskQueue;
-use crate::{arena::ScopeArena, innerlude::*};
+use crate::{arena::SharedArena, innerlude::*};
 use slotmap::DefaultKey;
 use slotmap::SlotMap;
 use std::{any::TypeId, fmt::Debug, rc::Rc};
@@ -35,7 +35,7 @@ pub struct VirtualDom {
     ///
     /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
     /// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
-    pub components: ScopeArena,
+    pub components: SharedArena,
 
     /// The index of the root component
     /// Should always be the first (gen=0, id=0)
@@ -48,7 +48,7 @@ pub struct VirtualDom {
 
     /// a strong allocation to the "caller" for the original component and its props
     #[doc(hidden)]
-    _root_caller: Rc<OpaqueComponent>,
+    _root_caller: Rc<WrappedCaller>,
 
     /// Type of the original cx. This is stored as TypeId so VirtualDom does not need to be generic.
     ///
@@ -58,22 +58,6 @@ pub struct VirtualDom {
     _root_prop_type: std::any::TypeId,
 }
 
-/// The `RealDomNode` is an ID handle that corresponds to a foreign DOM node.
-///
-/// "u64" was chosen for two reasons
-/// - 0 cost hashing
-/// - use with slotmap and other versioned slot arenas
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct RealDomNode(pub u64);
-impl RealDomNode {
-    pub fn new(id: u64) -> Self {
-        Self(id)
-    }
-    pub fn empty() -> Self {
-        Self(u64::MIN)
-    }
-}
-
 // ======================================
 // Public Methods for the VirtualDom
 // ======================================
@@ -142,11 +126,11 @@ impl VirtualDom {
     /// let dom = VirtualDom::new(Example);
     /// ```
     pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
-        let components = ScopeArena::new(SlotMap::new());
+        let components = SharedArena::new(SlotMap::new());
 
         // Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
         // Here, we need to make it manually, using an RC to force the Weak reference to stick around for the main scope.
-        let _root_caller: Rc<OpaqueComponent> = Rc::new(move |scope| {
+        let _root_caller: Rc<WrappedCaller> = Rc::new(move |scope| {
             // let _root_caller: Rc<OpaqueComponent<'static>> = Rc::new(move |scope| {
             // the lifetime of this closure is just as long as the lifetime on the scope reference
             // this closure moves root props (which is static) into this closure
@@ -283,12 +267,8 @@ impl VirtualDom {
         &'bump self,
         diff_machine: &'_ mut DiffMachine<'a, 'bump, Dom>,
     ) -> Result<()> {
-        // Add this component to the list of components that need to be difed
-        // #[allow(unused_assignments)]
-        // let mut cur_height: u32 = 0;
-
         // Now, there are events in the queue
-        let mut updates = self.event_queue.0.as_ref().borrow_mut();
+        let mut updates = self.event_queue.queue.as_ref().borrow_mut();
 
         // Order the nodes by their height, we want the nodes with the smallest depth on top
         // This prevents us from running the same component multiple times
@@ -299,6 +279,7 @@ impl VirtualDom {
         // Iterate through the triggered nodes (sorted by height) and begin to diff them
         for update in updates.drain(..) {
             log::debug!("Running updates for: {:#?}", update);
+
             // Make sure this isn't a node we've already seen, we don't want to double-render anything
             // If we double-renderer something, this would cause memory safety issues
             if diff_machine.seen_nodes.contains(&update.idx) {
@@ -310,20 +291,13 @@ impl VirtualDom {
 
             // Start a new mutable borrow to components
             // We are guaranteeed that this scope is unique because we are tracking which nodes have modified
-
             let cur_component = self.components.try_get_mut(update.idx).unwrap();
-            // let inner: &'s mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
-            // let cur_component = inner.get_mut(update.idx).unwrap();
 
             cur_component.run_scope()?;
-            // diff_machine.change_list.load_known_root(1);
 
             let (old, new) = (cur_component.old_frame(), cur_component.next_frame());
-            // let (old, new) = cur_component.get_frames_mut();
             diff_machine.diff_node(old, new);
 
-            // cur_height = cur_component.height;
-
             // log::debug!(
             //     "Processing update: {:#?} with height {}",
             //     &update.idx,

+ 61 - 0
packages/hooks/README.md

@@ -0,0 +1,61 @@
+# Common hooks for Dioxus
+
+This crate includes some basic useful hooks for dioxus:
+
+- use_state
+- use_ref
+- use_collection
+- use_task
+- use_signal
+
+## use_state
+
+The king daddy of state hooks.
+
+You can always use it "normally" with the `split` method:
+
+```rust
+// Normal usage:
+let value = use_state(cx, || 10);
+
+// "Classic" usage:
+let (value, set_value) = use_state(cx, || 0).classic();
+```
+
+## use_ref
+
+
+## use_rwlock
+A multithreaded form of RwLock for use in tasks
+```rust
+let val = use_rwlock(cx, || 10);
+use_task((), || async loop {
+    *val.write().unwrap() += 1;
+    async_std::task::delay(Duration::from_ms(1000)).await;
+});
+use_task((), || async loop {
+    *val.write().unwrap() -= 1;
+    async_std::task::delay(Duration::from_ms(500)).await;
+});
+```
+
+## use_hashmap
+Store a memoized collection with similar semantics to use_state. Comes with a bunch of utility methods to make working with collections easier. Is essentially a wrapper over the immutable hashmap in im-rc.
+
+```rust
+let todos = use_hashmap(cx, |map| map.insert("bob", "bill"));
+cx.render(rsx!(
+    button { onclick: move |_| todos.insert("bob", "bill")
+        "add random todo"
+    }
+)
+
+```
+
+## use_task
+
+use_task submits a task to the dioxus task queue to be progressed during Dioxus's async event loop. The task must not return anything
+
+
+## use_signal
+

+ 1 - 4
packages/hooks/examples/lifecycle.rs

@@ -1,7 +1,4 @@
-fn main() {
-    // let mut s = Context { props: &() };
-    // let g = Component(&mut s);
-}
+fn main() {}
 
 // struct CompState {
 //     tasks: Vec<()>,

+ 1 - 0
packages/hooks/examples/reducer.rs

@@ -0,0 +1 @@
+fn main() {}

+ 2 - 115
packages/hooks/src/lib.rs

@@ -2,118 +2,5 @@ use std::collections::HashMap;
 
 use dioxus_core::prelude::*;
 
-/*
-a form of use_state explicitly for map-style collections (BTreeMap, HashMap, etc).
-
-Why?
----
-Traditionally, it's possible to use the "use_state" hook for collections in the React world.
-Adding a new entry would look something similar to:
-
-```js
-let (map, set_map) = useState({});
-set_map({ ...map, [key]: value });
-```
-The new value then causes the appropriate update when passed into children.
-
-This is moderately efficient because the fields of the map are moved, but the data itself is not cloned.
-However, if you used similar approach with Dioxus:
-
-```rust
-let (map, set_map) = use_state(cx, || HashMap::new());
-set_map({
-    let mut newmap = map.clone();
-    newmap.set(key, value);
-    newmap
-})
-```
-Unfortunately, you'd be cloning the entire state every time a value is changed. The obvious solution is to
-wrap every element in the HashMap with an Rc. That way, cloning the HashMap is on par with its JS equivalent.
-
-Fortunately, we can make this operation even more efficient in Dioxus, leveraging the borrow rules of Rust.
-
-This hook provides a memoized collection, memoized setters, and memoized getters. This particular hook is
-extremely powerful for implementing lists and supporting core state management needs for small apps.
-
-If you need something even more powerful, check out the dedicated atomic state management Dioxus Dataflow, which
-uses the same memoization on top of the use_context API.
-
-Here's a fully-functional todo app using the use_map API:
-```rust
-static TodoList: FC<()> = |cx| {
-    let todos = use_map(cx, || HashMap::new());
-    let input = use_ref(|| None);
-
-    cx.render(rsx!{
-        div {
-            button {
-                "Add todo"
-                onclick: move |_| {
-                    let new_todo = TodoItem::new(input.contents());
-                    todos.insert(new_todo.id.clone(), new_todo);
-                    input.clear();
-                }
-            }
-            button {
-                "Clear todos"
-                onclick: move |_| todos.clear()
-            }
-            input {
-                placeholder: "What needs to be done?"
-                ref: input
-            }
-            ul {
-                {todos.iter().map(|todo| rsx!(
-                    li {
-                        key: todo.id
-                        span { "{todo.content}" }
-                        button {"x", onclick: move |_| todos.remove(todo.key.clone())}
-                    }
-                ))}
-            }
-        }
-    })
-}
-
-```
-
-*/
-fn use_map() {}
-
-// a form of "use_state" that allows collection memoization
-// Elements are received as Rc<T> in case the underlying collection is shuffled around
-// Setters/getters can be generated
-fn use_collection<'a, T: Collection>(
-    cx: &impl Scoped<'a>,
-    f: impl Fn() -> T,
-) -> CollectionHandle<'a, T> {
-    cx.use_hook(
-        || {},
-        |h| {
-            //
-            CollectionHandle {
-                _p: Default::default(),
-            }
-        },
-        |h| {},
-    )
-}
-
-struct CollectionMemo {}
-
-struct CollectionHandle<'a, T: Collection> {
-    _p: std::marker::PhantomData<&'a T>,
-}
-
-trait Collection {}
-impl<K, V> Collection for std::collections::HashMap<K, V> {}
-
-struct MapCollection<K, V> {
-    inner: HashMap<K, V>,
-}
-
-impl<K, V> MapCollection<K, V> {
-    fn set(&self, key: K, val: V) {
-        //
-    }
-}
+mod usestate;
+pub use usestate::{use_state, UseState};

+ 76 - 0
packages/hooks/src/usecollection.rs

@@ -0,0 +1,76 @@
+/*
+a form of use_state explicitly for map-style collections (BTreeMap, HashMap, etc).
+
+Why?
+---
+Traditionally, it's possible to use the "use_state" hook for collections in the React world.
+Adding a new entry would look something similar to:
+
+```js
+let (map, set_map) = useState({});
+set_map({ ...map, [key]: value });
+```
+The new value then causes the appropriate update when passed into children.
+
+This is moderately efficient because the fields of the map are moved, but the data itself is not cloned.
+However, if you used similar approach with Dioxus:
+
+```rust
+let (map, set_map) = use_state(cx, || HashMap::new());
+set_map({
+    let mut newmap = map.clone();
+    newmap.set(key, value);
+    newmap
+})
+```
+Unfortunately, you'd be cloning the entire state every time a value is changed. The obvious solution is to
+wrap every element in the HashMap with an Rc. That way, cloning the HashMap is on par with its JS equivalent.
+
+Fortunately, we can make this operation even more efficient in Dioxus, leveraging the borrow rules of Rust.
+
+This hook provides a memoized collection, memoized setters, and memoized getters. This particular hook is
+extremely powerful for implementing lists and supporting core state management needs for small apps.
+
+If you need something even more powerful, check out the dedicated atomic state management Dioxus Dataflow, which
+uses the same memoization on top of the use_context API.
+
+Here's a fully-functional todo app using the use_map API:
+```rust
+static TodoList: FC<()> = |cx| {
+    let todos = use_map(cx, || HashMap::new());
+    let input = use_ref(|| None);
+
+    cx.render(rsx!{
+        div {
+            button {
+                "Add todo"
+                onclick: move |_| {
+                    let new_todo = TodoItem::new(input.contents());
+                    todos.insert(new_todo.id.clone(), new_todo);
+                    input.clear();
+                }
+            }
+            button {
+                "Clear todos"
+                onclick: move |_| todos.clear()
+            }
+            input {
+                placeholder: "What needs to be done?"
+                ref: input
+            }
+            ul {
+                {todos.iter().map(|todo| rsx!(
+                    li {
+                        key: todo.id
+                        span { "{todo.content}" }
+                        button {"x", onclick: move |_| todos.remove(todo.key.clone())}
+                    }
+                ))}
+            }
+        }
+    })
+}
+
+```
+
+*/

+ 0 - 0
packages/hooks/src/useref.rs


+ 0 - 0
packages/hooks/src/usesignal.rs


+ 170 - 0
packages/hooks/src/usestate.rs

@@ -0,0 +1,170 @@
+use dioxus_core::prelude::Context;
+use std::{
+    cell::{Cell, Ref, RefCell, RefMut},
+    fmt::Display,
+    ops::{Deref, DerefMut},
+    rc::Rc,
+};
+
+/// Store state between component renders!
+///
+/// ## The "King" of state hooks
+///
+/// The Dioxus version of `useState` is the "king daddy" of state management. It allows you to ergonomically store and
+/// modify state between component renders. When the state is updated, the component will re-render.
+///
+/// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
+///
+/// [`use_state`] exposes a few helper methods to modify the underlying state:
+/// - `.set(new)` allows you to override the "work in progress" value with a new value
+/// - `.get_mut()` allows you to modify the WIP value
+/// - `.get_wip()` allows you to access the WIP value
+/// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required)
+///
+/// Additionally, a ton of std::ops traits are implemented for the `UseState` wrapper, meaning any mutative type operations
+/// will automatically be called on the WIP value.
+///
+///
+/// Usage:
+/// ```ignore
+/// const Example: FC<()> = |cx| {
+///     let counter = use_state(cx, || 0);
+///     let increment = |_| counter += 1;
+///     let decrement = |_| counter += 1;
+///
+///     html! {
+///         <div>
+///             <h1>"Counter: {counter}" </h1>
+///             <button onclick={increment}> "Increment" </button>
+///             <button onclick={decrement}> "Decrement" </button>
+///         </div>  
+///     }
+/// }
+/// ```
+pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
+    cx: Context<'a, P>,
+    initial_state_fn: F,
+) -> UseState<T> {
+    cx.use_hook(
+        move || UseStateInner {
+            current_val: initial_state_fn(),
+            callback: cx.schedule_update(),
+            wip: RefCell::new(None),
+            update_scheuled: Cell::new(false),
+        },
+        move |hook| {
+            hook.update_scheuled.set(false);
+            let mut new_val = hook.wip.borrow_mut();
+            if new_val.is_some() {
+                hook.current_val = new_val.take().unwrap();
+            }
+
+            UseState { inner: &*hook }
+        },
+        |_| {},
+    )
+}
+struct UseStateInner<T: 'static> {
+    current_val: T,
+    update_scheuled: Cell<bool>,
+    callback: Rc<dyn Fn()>,
+    wip: RefCell<Option<T>>,
+    updater: 
+}
+
+pub struct UseState<'a, T: 'static> {
+    inner: &'a UseStateInner<T>,
+}
+impl<T> Copy for UseState<'_, T> {}
+impl<'a, T> Clone for UseState<'a, T>
+where
+    T: 'static,
+{
+    fn clone(&self) -> Self {
+        UseState { inner: self.inner }
+    }
+}
+
+impl<'a, T: 'static> UseState<'a, T> {
+    /// Tell the Dioxus Scheduler that we need to be processed
+    pub fn needs_update(&self) {
+        if !self.inner.update_scheuled.get() {
+            self.inner.update_scheuled.set(true);
+            (self.inner.callback)();
+        }
+    }
+
+    pub fn set(&self, new_val: T) {
+        self.needs_update();
+        *self.inner.wip.borrow_mut() = Some(new_val);
+    }
+
+    pub fn get(&self) -> &T {
+        &self.inner.current_val
+    }
+
+    /// Get the current status of the work-in-progress data
+    pub fn get_wip(&self) -> Ref<Option<T>> {
+        self.inner.wip.borrow()
+    }
+
+    pub fn classic(self) -> (&'a T, &'a Rc<dyn Fn(T)>) {
+        (&self.inner.current_val)
+    }
+}
+impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
+    pub fn get_mut(self) -> RefMut<'a, T> {
+        // make sure we get processed
+        self.needs_update();
+
+        // Bring out the new value, cloning if it we need to
+        // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
+        RefMut::map(self.inner.wip.borrow_mut(), |slot| {
+            if slot.is_none() {
+                *slot = Some(self.inner.current_val.to_owned());
+            }
+            slot.as_mut().unwrap()
+        })
+    }
+}
+
+impl<'a, T: 'static> std::ops::Deref for UseState<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner.current_val
+    }
+}
+
+use std::ops::{Add, AddAssign, Sub, SubAssign};
+impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseState<'a, T> {
+    type Output = T;
+
+    fn add(self, rhs: T) -> Self::Output {
+        self.inner.current_val.add(rhs)
+    }
+}
+impl<'a, T: Copy + Add<T, Output = T>> AddAssign<T> for UseState<'a, T> {
+    fn add_assign(&mut self, rhs: T) {
+        self.set(self.inner.current_val.add(rhs));
+    }
+}
+impl<'a, T: Copy + Sub<T, Output = T>> Sub<T> for UseState<'a, T> {
+    type Output = T;
+
+    fn sub(self, rhs: T) -> Self::Output {
+        self.inner.current_val.sub(rhs)
+    }
+}
+impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseState<'a, T> {
+    fn sub_assign(&mut self, rhs: T) {
+        self.set(self.inner.current_val.sub(rhs));
+    }
+}
+
+// enable displaty for the handle
+impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.inner.current_val)
+    }
+}

+ 0 - 0
packages/hooks/src/usetask.rs