Przeglądaj źródła

wip: use_reducer

Jonathan Kelley 4 lat temu
rodzic
commit
879e107
1 zmienionych plików z 94 dodań i 0 usunięć
  1. 94 0
      packages/core/src/hooks.rs

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

@@ -117,3 +117,97 @@ mod use_ref_def {
         ctx.use_hook(|| UseRef::new(initial_state_fn()), |state| &*state, |_| {})
     }
 }
+
+mod use_reducer_def {
+    use crate::innerlude::*;
+    use std::{cell::RefCell, ops::DerefMut, rc::Rc};
+
+    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>(
+        ctx: &'c Context<'a>,
+        initial_state_fn: impl FnOnce() -> State,
+        reducer: impl Fn(&mut State, Action),
+    ) -> (&'a State, &'a impl Fn(Action)) {
+        ctx.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 = 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)
+            },
+            |_| {},
+        )
+    }
+
+    // #[cfg(test)]
+    mod tests {
+        use super::*;
+        use crate::prelude::*;
+        use bumpalo::Bump;
+
+        enum Actions {
+            Incr,
+            Decr,
+        }
+
+        #[allow(unused)]
+        static Example: FC<()> = |ctx, props| {
+            let (count, reduce) = use_reducer(
+                &ctx,
+                || 0,
+                |count, action| match action {
+                    Actions::Incr => *count += 1,
+                    Actions::Decr => *count -= 1,
+                },
+            );
+
+            ctx.render(rsx! {
+                div {
+                    h1 {"Count: {count}"}
+                    button {
+                        "Increment"
+                        onclick: move |_| reduce(Actions::Incr)
+                    }
+                    button {
+                        "Decrement"
+                        onclick: move |_| reduce(Actions::Decr)
+                    }
+                }
+            })
+        };
+    }
+}